Skip to content

Commit

Permalink
Remove rows
Browse files Browse the repository at this point in the history
  • Loading branch information
zurfyx committed Mar 11, 2023
1 parent 52a7253 commit 0e2ae86
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 11 deletions.
57 changes: 57 additions & 0 deletions packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
click,
clickSelectors,
copyToClipboard,
deleteTableRows,
focusEditor,
html,
initialize,
Expand Down Expand Up @@ -1218,4 +1219,60 @@ test.describe('Tables', () => {
`,
);
});

test('Delete rows (with conflicting merged cell)', async ({
page,
isPlainText,
}) => {
test.skip(isPlainText);

await focusEditor(page);

await insertTable(page, 4, 2);

await selectCellsFromTableCords(
page,
{x: 1, y: 1},
{x: 1, y: 3},
false,
false,
);
await mergeTableCells(page);

await page.pause();
await selectCellsFromTableCords(
page,
{x: 0, y: 0},
{x: 0, y: 1},
true,
true,
);

await deleteTableRows(page);

await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<table class="PlaygroundEditorTheme__table">
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<td class="PlaygroundEditorTheme__tableCell" rowspan="2">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
</tr>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
</tr>
</table>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,15 @@ export async function moveRight(page, numCharacters = 1, delayMs) {
}
}

export async function moveUp(page, numCharacters = 1, delayMs) {
for (let i = 0; i < numCharacters; i++) {
if (delayMs !== undefined) {
await sleep(delayMs);
}
await page.keyboard.press('ArrowUp');
}
}

export async function moveDown(page, numCharacters = 1, delayMs) {
for (let i = 0; i < numCharacters; i++) {
if (delayMs !== undefined) {
Expand Down
15 changes: 15 additions & 0 deletions packages/lexical-playground/__tests__/utils/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,21 @@ export async function mergeTableCells(page) {
await click(page, '.item[data-test-id="table-merge-cells"]');
}

export async function deleteTableRows(page) {
await click(page, '.table-cell-action-button-container');
await click(page, '.item[data-test-id="table-delete-rows"]');
}

export async function deleteTableColumns(page) {
await click(page, '.table-cell-action-button-container');
await click(page, '.item[data-test-id="table-delete-columns"]');
}

export async function deleteTable(page) {
await click(page, '.table-cell-action-button-container');
await click(page, '.item[data-test-id="table-delete"]');
}

export async function enableCompositionKeyEvents(page) {
const targetPage = IS_COLLAB ? await page.frame('left') : page;
await targetPage.evaluate(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import useLexicalEditable from '@lexical/react/useLexicalEditable';
import {
$deleteTableColumn,
$deleteTableRow__EXPERIMENTAL,
$getTableCellNodeFromLexicalNode,
$getTableColumnIndexFromTableCellNode,
$getTableNodeFromLexicalNodeOrThrow,
Expand All @@ -18,7 +19,6 @@ import {
$insertTableRow__EXPERIMENTAL,
$isTableCellNode,
$isTableRowNode,
$removeTableRowAtIndex,
getTableSelectionFromTableElement,
HTMLTableElementWithWithTableSelectionState,
TableCellHeaderStates,
Expand Down Expand Up @@ -251,15 +251,10 @@ function TableActionMenu({

const deleteTableRowAtSelection = useCallback(() => {
editor.update(() => {
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
const tableRowIndex = $getTableRowIndexFromTableCellNode(tableCellNode);

$removeTableRowAtIndex(tableNode, tableRowIndex);

clearTableSelection();
$deleteTableRow__EXPERIMENTAL();
onClose();
});
}, [editor, tableCellNode, clearTableSelection, onClose]);
}, [editor, onClose]);

const deleteTableAtSelection = useCallback(() => {
editor.update(() => {
Expand Down Expand Up @@ -419,13 +414,22 @@ function TableActionMenu({
</span>
</button>
<hr />
<button className="item" onClick={() => deleteTableColumnAtSelection()}>
<button
className="item"
onClick={() => deleteTableColumnAtSelection()}
data-test-id="table-delete-columns">
<span className="text">Delete column</span>
</button>
<button className="item" onClick={() => deleteTableRowAtSelection()}>
<button
className="item"
onClick={() => deleteTableRowAtSelection()}
data-test-id="table-delete-rows">
<span className="text">Delete row</span>
</button>
<button className="item" onClick={() => deleteTableAtSelection()}>
<button
className="item"
onClick={() => deleteTableAtSelection()}
data-test-id="table-delete">
<span className="text">Delete table</span>
</button>
<hr />
Expand Down
95 changes: 95 additions & 0 deletions packages/lexical-table/src/LexicalTableUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import type {Grid} from './LexicalTableSelection';
import type {ElementNode} from 'lexical';

import {$findMatchingParent} from '@lexical/utils';
import {
Expand All @@ -18,6 +19,7 @@ import {
DEPRECATED_$getNodeTriplet,
DEPRECATED_$isGridRowNode,
DEPRECATED_$isGridSelection,
DEPRECATED_GridCellNode,
LexicalNode,
} from 'lexical';
import invariant from 'shared/invariant';
Expand Down Expand Up @@ -394,3 +396,96 @@ export function $deleteTableColumn(

return tableNode;
}

export function $deleteTableRow__EXPERIMENTAL(): void {
const selection = $getSelection();
invariant(
$isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection),
'Expected a RangeSelection or GridSelection',
);
const anchor = selection.anchor.getNode();
const focus = selection.focus.getNode();
const [anchorCell, , grid] = DEPRECATED_$getNodeTriplet(anchor);
const [focusCell] = DEPRECATED_$getNodeTriplet(focus);
const [gridMap, anchorCellMap, focusCellMap] = DEPRECATED_$computeGridMap(
grid,
anchorCell,
focusCell,
);
const {startRow: anchorStartRow} = anchorCellMap;
const {startRow: focusStartRow} = focusCellMap;
const focusEndRow = focusStartRow + focusCell.__rowSpan - 1;
if (gridMap.length === focusEndRow - anchorStartRow + 1) {
// Empty grid
grid.remove();
return;
}
const columnCount = gridMap[0].length;
const nextRow = gridMap[focusEndRow + 1];
const nextRowNode = grid.getChildAtIndex(focusEndRow + 1);
invariant(
DEPRECATED_$isGridRowNode(nextRowNode),
'Expected GridNode childAtIndex(%s) to be RowNode',
String(focusEndRow + 1),
);
for (let row = focusEndRow; row >= anchorStartRow; row--) {
for (let column = columnCount - 1; column >= 0; column--) {
const {
cell,
startRow: cellStartRow,
startColumn: cellStartColumn,
} = gridMap[row][column];
if (cellStartColumn !== column) {
// Don't repeat work for the same Cell
continue;
}
// Rows overflowing top have to be trimmed
if (row === anchorStartRow && cellStartRow < anchorStartRow) {
cell.setRowSpan(cell.__rowSpan - (cellStartRow - anchorStartRow));
}
// Rows overflowing bottom have to be trimmed and moved to the next row
if (
cellStartRow >= anchorStartRow &&
cellStartRow + cell.__rowSpan - 1 > focusEndRow
) {
cell.setRowSpan(cell.__rowSpan - (focusEndRow - cellStartRow + 1));
if (column === 0) {
$insertFirst(nextRowNode, cell);
} else {
const {cell: previousCell} = nextRow[column - 1];
previousCell.insertAfter(cell);
}
}
}
const rowNode = grid.getChildAtIndex(row);
invariant(
DEPRECATED_$isGridRowNode(rowNode),
'Expected GridNode childAtIndex(%s) to be RowNode',
String(row),
);
rowNode.remove();
}
if (nextRow !== undefined) {
const {cell} = nextRow[0];
$moveSelectionToCell(cell);
} else {
const previousRow = gridMap[anchorStartRow - 1];
const {cell} = previousRow[0];
$moveSelectionToCell(cell);
}
}

function $moveSelectionToCell(cell: DEPRECATED_GridCellNode): void {
const firstDescendant = cell.getFirstDescendant();
invariant(firstDescendant !== null, 'Unexpected empty cell');
firstDescendant.getParentOrThrow().selectStart();
}

function $insertFirst(parent: ElementNode, node: LexicalNode): void {
const firstChild = parent.getFirstChild();
if (firstChild !== null) {
parent.insertBefore(firstChild);
} else {
parent.append(node);
}
}
2 changes: 2 additions & 0 deletions packages/lexical-table/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
import {
$createTableNodeWithDimensions,
$deleteTableColumn,
$deleteTableRow__EXPERIMENTAL,
$getTableCellNodeFromLexicalNode,
$getTableColumnIndexFromTableCellNode,
$getTableNodeFromLexicalNodeOrThrow,
Expand All @@ -60,6 +61,7 @@ export {
$createTableNodeWithDimensions,
$createTableRowNode,
$deleteTableColumn,
$deleteTableRow__EXPERIMENTAL,
$getElementGridForTableNode,
$getTableCellNodeFromLexicalNode,
$getTableColumnIndexFromTableCellNode,
Expand Down

0 comments on commit 0e2ae86

Please sign in to comment.