diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
index 41aa0243f11..a8df457e4b7 100644
--- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
+++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
@@ -41,7 +41,6 @@ import {
selectCellsFromTableCords,
selectFromAdditionalStylesDropdown,
selectFromAlignDropdown,
- selectorBoundingBox,
setBackgroundColor,
test,
toggleColumnHeader,
@@ -1521,232 +1520,6 @@ test.describe('Tables', () => {
);
});
- test('Resize merged cells width (1)', async ({
- page,
- isPlainText,
- isCollab,
- }) => {
- await initialize({isCollab, page});
- test.skip(isPlainText);
- if (IS_COLLAB) {
- // The contextual menu positioning needs fixing (it's hardcoded to show on the right side)
- page.setViewportSize({height: 1000, width: 3000});
- }
-
- await focusEditor(page);
-
- await insertTable(page, 3, 3);
- await click(page, '.PlaygroundEditorTheme__tableCell');
- await selectCellsFromTableCords(
- page,
- {x: 0, y: 0},
- {x: 1, y: 1},
- true,
- false,
- );
- await mergeTableCells(page);
- await click(page, 'td:nth-child(3) > .PlaygroundEditorTheme__paragraph');
- const resizerBoundingBox = await selectorBoundingBox(
- page,
- '.TableCellResizer__resizer:first-child',
- );
- const x = resizerBoundingBox.x + resizerBoundingBox.width / 2;
- const y = resizerBoundingBox.y + resizerBoundingBox.height / 2;
- await page.mouse.move(x, y);
- await page.mouse.down();
- await page.mouse.move(x + 50, y);
- await page.mouse.up();
-
- await assertHTML(
- page,
- html`
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
- |
-
-
- |
-
-
-
- `,
- );
- });
-
- test('Resize merged cells width (2)', async ({
- page,
- isPlainText,
- isCollab,
- }) => {
- await initialize({isCollab, page});
- test.skip(isPlainText);
- if (IS_COLLAB) {
- // The contextual menu positioning needs fixing (it's hardcoded to show on the right side)
- page.setViewportSize({height: 1000, width: 3000});
- }
-
- await focusEditor(page);
-
- await insertTable(page, 3, 3);
- await click(page, '.PlaygroundEditorTheme__tableCell');
- await selectCellsFromTableCords(
- page,
- {x: 0, y: 0},
- {x: 1, y: 1},
- true,
- false,
- );
- await mergeTableCells(page);
- await page.locator('th').first().click();
- const resizerBoundingBox = await selectorBoundingBox(
- page,
- '.TableCellResizer__resizer:first-child',
- );
- const x = resizerBoundingBox.x + resizerBoundingBox.width / 2;
- const y = resizerBoundingBox.y + resizerBoundingBox.height / 2;
- await page.mouse.move(x, y);
- await page.mouse.down();
- await page.mouse.move(x + 50, y);
- await page.mouse.up();
-
- await assertHTML(
- page,
- html`
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
- |
-
-
- |
-
-
-
- `,
- );
- });
-
- test('Resize merged cells height', async ({page, isPlainText, isCollab}) => {
- await initialize({isCollab, page});
- test.skip(isPlainText);
- if (IS_COLLAB) {
- // The contextual menu positioning needs fixing (it's hardcoded to show on the right side)
- page.setViewportSize({height: 1000, width: 3000});
- }
-
- await focusEditor(page);
-
- await insertTable(page, 3, 3);
- await click(page, '.PlaygroundEditorTheme__tableCell');
- await selectCellsFromTableCords(
- page,
- {x: 0, y: 0},
- {x: 1, y: 1},
- true,
- false,
- );
- await mergeTableCells(page);
- await page.locator('th').first().click();
- const resizerBoundingBox = await selectorBoundingBox(
- page,
- '.TableCellResizer__resizer:nth-child(2)',
- );
- const x = resizerBoundingBox.x + resizerBoundingBox.width / 2;
- const y = resizerBoundingBox.y + resizerBoundingBox.height / 2;
- await page.mouse.move(x, y);
- await page.mouse.down();
- await page.mouse.move(x, y + 50);
- await page.mouse.up();
-
- await assertHTML(
- page,
- html`
-
-
-
-
-
-
-
-
-
- |
-
-
-
-
-
- |
-
-
- |
-
-
-
- `,
- );
- });
-
test('Merge/unmerge cells (1)', async ({page, isPlainText, isCollab}) => {
await initialize({isCollab, page});
test.skip(isPlainText);
diff --git a/packages/lexical-playground/src/plugins/TableCellResizer/index.tsx b/packages/lexical-playground/src/plugins/TableCellResizer/index.tsx
index 43290761f08..bc96ffe81e0 100644
--- a/packages/lexical-playground/src/plugins/TableCellResizer/index.tsx
+++ b/packages/lexical-playground/src/plugins/TableCellResizer/index.tsx
@@ -5,12 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
*/
-import type {
- TableCellNode,
- TableDOMCell,
- TableMapType,
- TableMapValueType,
-} from '@lexical/table';
+import type {TableDOMCell} from '@lexical/table';
import type {LexicalEditor} from 'lexical';
import './index.css';
@@ -18,12 +13,13 @@ import './index.css';
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {useLexicalEditable} from '@lexical/react/useLexicalEditable';
import {
- $computeTableMapSkipCellCheck,
+ $getTableColumnIndexFromTableCellNode,
$getTableNodeFromLexicalNodeOrThrow,
$getTableRowIndexFromTableCellNode,
$isTableCellNode,
$isTableRowNode,
getDOMCellFromTarget,
+ TableCellNode,
} from '@lexical/table';
import {calculateZoomLevel} from '@lexical/utils';
import {$getNearestNodeFromDOMNode} from 'lexical';
@@ -153,7 +149,7 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
};
const updateRowHeight = useCallback(
- (heightChange: number) => {
+ (newHeight: number) => {
if (!activeCell) {
throw new Error('TableCellResizer: Expected active cell.');
}
@@ -182,17 +178,6 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
throw new Error('Expected table row');
}
- let height = tableRow.getHeight();
- if (height === undefined) {
- const rowCells = tableRow.getChildren();
- height = Math.min(
- ...rowCells.map(
- (cell) => getCellNodeHeight(cell, editor) ?? Infinity,
- ),
- );
- }
-
- const newHeight = Math.max(height + heightChange, MIN_ROW_HEIGHT);
tableRow.setHeight(newHeight);
},
{tag: 'skip-scroll-into-view'},
@@ -201,50 +186,8 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
[activeCell, editor],
);
- const getCellNodeWidth = (
- cell: TableCellNode,
- activeEditor: LexicalEditor,
- ): number | undefined => {
- const width = cell.getWidth();
- if (width !== undefined) {
- return width;
- }
-
- const domCellNode = activeEditor.getElementByKey(cell.getKey());
- if (domCellNode == null) {
- return undefined;
- }
- const computedStyle = getComputedStyle(domCellNode);
- return (
- domCellNode.clientWidth -
- parseFloat(computedStyle.paddingLeft) -
- parseFloat(computedStyle.paddingRight)
- );
- };
-
- const getCellNodeHeight = (
- cell: TableCellNode,
- activeEditor: LexicalEditor,
- ): number | undefined => {
- const domCellNode = activeEditor.getElementByKey(cell.getKey());
- return domCellNode?.clientHeight;
- };
-
- const getCellColumnIndex = (
- tableCellNode: TableCellNode,
- tableMap: TableMapType,
- ) => {
- for (let row = 0; row < tableMap.length; row++) {
- for (let column = 0; column < tableMap[row].length; column++) {
- if (tableMap[row][column].cell === tableCellNode) {
- return column;
- }
- }
- }
- };
-
const updateColumnWidth = useCallback(
- (widthChange: number) => {
+ (newWidth: number) => {
if (!activeCell) {
throw new Error('TableCellResizer: Expected active cell.');
}
@@ -256,31 +199,48 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
}
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
- const [tableMap] = $computeTableMapSkipCellCheck(
- tableNode,
- null,
- null,
- );
- const columnIndex = getCellColumnIndex(tableCellNode, tableMap);
- if (columnIndex === undefined) {
- throw new Error('TableCellResizer: Table column not found.');
- }
- for (let row = 0; row < tableMap.length; row++) {
- const cell: TableMapValueType = tableMap[row][columnIndex];
+ const tableColumnIndex =
+ $getTableColumnIndexFromTableCellNode(tableCellNode);
+
+ const tableRows = tableNode.getChildren();
+
+ for (let r = 0; r < tableRows.length; r++) {
+ const tableRow = tableRows[r];
+
+ if (!$isTableRowNode(tableRow)) {
+ throw new Error('Expected table row');
+ }
+
+ const rowCells = tableRow.getChildren();
+ const rowCellsSpan = rowCells.map((cell) => cell.getColSpan());
+
+ const aggregatedRowSpans = rowCellsSpan.reduce(
+ (rowSpans: number[], cellSpan) => {
+ const previousCell = rowSpans[rowSpans.length - 1] ?? 0;
+ rowSpans.push(previousCell + cellSpan);
+ return rowSpans;
+ },
+ [],
+ );
+ const rowColumnIndexWithSpan = aggregatedRowSpans.findIndex(
+ (cellSpan: number) => cellSpan > tableColumnIndex,
+ );
+
if (
- cell.startRow === row &&
- (columnIndex === tableMap[row].length - 1 ||
- tableMap[row][columnIndex].cell !==
- tableMap[row][columnIndex + 1].cell)
+ rowColumnIndexWithSpan >= rowCells.length ||
+ rowColumnIndexWithSpan < 0
) {
- const width = getCellNodeWidth(cell.cell, editor);
- if (width === undefined) {
- continue;
- }
- const newWidth = Math.max(width + widthChange, MIN_COLUMN_WIDTH);
- cell.cell.setWidth(newWidth);
+ throw new Error('Expected table cell to be inside of table row.');
}
+
+ const tableCell = rowCells[rowColumnIndexWithSpan];
+
+ if (!$isTableCellNode(tableCell)) {
+ throw new Error('Expected table cell');
+ }
+
+ tableCell.setWidth(newWidth);
}
},
{tag: 'skip-scroll-into-view'},
@@ -308,11 +268,33 @@ function TableCellResizer({editor}: {editor: LexicalEditor}): JSX.Element {
const zoom = calculateZoomLevel(event.target as Element);
if (isHeightChanging(direction)) {
- const heightChange = (event.clientY - y) / zoom;
- updateRowHeight(heightChange);
+ const height = activeCell.elem.getBoundingClientRect().height;
+ const heightChange = Math.abs(event.clientY - y) / zoom;
+
+ const isShrinking = direction === 'bottom' && y > event.clientY;
+
+ updateRowHeight(
+ Math.max(
+ isShrinking ? height - heightChange : heightChange + height,
+ MIN_ROW_HEIGHT,
+ ),
+ );
} else {
- const widthChange = (event.clientX - x) / zoom;
- updateColumnWidth(widthChange);
+ const computedStyle = getComputedStyle(activeCell.elem);
+ let width = activeCell.elem.clientWidth; // width with padding
+ width -=
+ parseFloat(computedStyle.paddingLeft) +
+ parseFloat(computedStyle.paddingRight);
+ const widthChange = Math.abs(event.clientX - x) / zoom;
+
+ const isShrinking = direction === 'right' && x > event.clientX;
+
+ updateColumnWidth(
+ Math.max(
+ isShrinking ? width - widthChange : widthChange + width,
+ MIN_COLUMN_WIDTH,
+ ),
+ );
}
resetState();
diff --git a/packages/lexical-table/src/index.ts b/packages/lexical-table/src/index.ts
index 41462c0237e..c9f3964cf07 100644
--- a/packages/lexical-table/src/index.ts
+++ b/packages/lexical-table/src/index.ts
@@ -34,8 +34,6 @@ export {
TableRowNode,
} from './LexicalTableRowNode';
export type {
- TableMapType,
- TableMapValueType,
TableSelection,
TableSelectionShape,
} from './LexicalTableSelection';