import {BaseSelection, LexicalEditor} from "lexical";
-import {$isTableRowNode, $isTableSelection, TableRowNode} from "@lexical/table";
+import {$isTableRowNode, $isTableSelection, TableRowNode, TableSelection, TableSelectionShape} from "@lexical/table";
import {$isCustomTableNode, CustomTableNode} from "../nodes/custom-table";
-import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell-node";
+import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
import {$getParentOfType} from "./nodes";
import {$getNodeFromSelection} from "./selection";
import {formatSizeValue} from "./dom";
+import {TableMap} from "./table-map";
+import {$isCustomTableRowNode, CustomTableRowNode} from "../nodes/custom-table-row";
function $getTableFromCell(cell: CustomTableCellNode): CustomTableNode|null {
return $getParentOfType(cell, $isCustomTableNode) as CustomTableNode|null;
}
}
+export function $getTableCellColumnWidth(editor: LexicalEditor, cell: CustomTableCellNode): string {
+ const table = $getTableFromCell(cell)
+ const index = $getCellColumnIndex(cell);
+ if (!table) {
+ return '';
+ }
+
+ const widths = table.getColWidths();
+ return (widths.length > index) ? widths[index] : '';
+}
+
export function $getTableCellsFromSelection(selection: BaseSelection|null): CustomTableCellNode[] {
if ($isTableSelection(selection)) {
const nodes = selection.getNodes();
const cell = $getNodeFromSelection(selection, $isCustomTableCellNode) as CustomTableCellNode;
return cell ? [cell] : [];
-}
\ No newline at end of file
+}
+
+export function $mergeTableCellsInSelection(selection: TableSelection): void {
+ const selectionShape = selection.getShape();
+ const cells = $getTableCellsFromSelection(selection);
+ if (cells.length === 0) {
+ return;
+ }
+
+ const table = $getTableFromCell(cells[0]);
+ if (!table) {
+ return;
+ }
+
+ const tableMap = new TableMap(table);
+ const headCell = tableMap.getCellAtPosition(selectionShape.toX, selectionShape.toY);
+ if (!headCell) {
+ return;
+ }
+
+ // We have to adjust the shape since it won't take into account spans for the head corner position.
+ const fixedToX = selectionShape.toX + ((headCell.getColSpan() || 1) - 1);
+ const fixedToY = selectionShape.toY + ((headCell.getRowSpan() || 1) - 1);
+
+ const mergeCells = tableMap.getCellsInRange({
+ fromX: selectionShape.fromX,
+ fromY: selectionShape.fromY,
+ toX: fixedToX,
+ toY: fixedToY,
+ });
+
+ if (mergeCells.length === 0) {
+ return;
+ }
+
+ const firstCell = mergeCells[0];
+ const newWidth = Math.abs(selectionShape.fromX - fixedToX) + 1;
+ const newHeight = Math.abs(selectionShape.fromY - fixedToY) + 1;
+
+ for (let i = 1; i < mergeCells.length; i++) {
+ const mergeCell = mergeCells[i];
+ firstCell.append(...mergeCell.getChildren());
+ mergeCell.remove();
+ }
+
+ firstCell.setColSpan(newWidth);
+ firstCell.setRowSpan(newHeight);
+}
+
+export function $getTableRowsFromSelection(selection: BaseSelection|null): CustomTableRowNode[] {
+ const cells = $getTableCellsFromSelection(selection);
+ const rowsByKey: Record<string, CustomTableRowNode> = {};
+ for (const cell of cells) {
+ const row = cell.getParent();
+ if ($isCustomTableRowNode(row)) {
+ rowsByKey[row.getKey()] = row;
+ }
+ }
+
+ return Object.values(rowsByKey);
+}
+
+export function $getTableFromSelection(selection: BaseSelection|null): CustomTableNode|null {
+ const cells = $getTableCellsFromSelection(selection);
+ if (cells.length === 0) {
+ return null;
+ }
+
+ const table = $getParentOfType(cells[0], $isCustomTableNode);
+ if ($isCustomTableNode(table)) {
+ return table;
+ }
+
+ return null;
+}
+
+export function $clearTableSizes(table: CustomTableNode): void {
+ table.setColWidths([]);
+
+ // TODO - Extra form things once table properties and extra things
+ // are supported
+
+ for (const row of table.getChildren()) {
+ if (!$isCustomTableRowNode(row)) {
+ continue;
+ }
+
+ const rowStyles = row.getStyles();
+ rowStyles.delete('height');
+ rowStyles.delete('width');
+ row.setStyles(rowStyles);
+
+ const cells = row.getChildren().filter(c => $isCustomTableCellNode(c));
+ for (const cell of cells) {
+ const cellStyles = cell.getStyles();
+ cellStyles.delete('height');
+ cellStyles.delete('width');
+ cell.setStyles(cellStyles);
+ cell.clearWidth();
+ }
+ }
+}
+
+export function $clearTableFormatting(table: CustomTableNode): void {
+ table.setColWidths([]);
+ table.setStyles(new Map);
+
+ for (const row of table.getChildren()) {
+ if (!$isCustomTableRowNode(row)) {
+ continue;
+ }
+
+ row.setStyles(new Map);
+ row.setFormat('');
+
+ const cells = row.getChildren().filter(c => $isCustomTableCellNode(c));
+ for (const cell of cells) {
+ cell.setStyles(new Map);
+ cell.clearWidth();
+ cell.setFormat('');
+ }
+ }
+}
+
+/**
+ * Perform the given callback for each cell in the given table.
+ * Returning false from the callback stops the function early.
+ */
+export function $forEachTableCell(table: CustomTableNode, callback: (c: CustomTableCellNode) => void|false): void {
+ outer: for (const row of table.getChildren()) {
+ if (!$isCustomTableRowNode(row)) {
+ continue;
+ }
+ const cells = row.getChildren();
+ for (const cell of cells) {
+ if (!$isCustomTableCellNode(cell)) {
+ return;
+ }
+ const result = callback(cell);
+ if (result === false) {
+ break outer;
+ }
+ }
+ }
+}
+
+export function $getCellPaddingForTable(table: CustomTableNode): string {
+ let padding: string|null = null;
+
+ $forEachTableCell(table, (cell: CustomTableCellNode) => {
+ const cellPadding = cell.getStyles().get('padding') || ''
+ if (padding === null) {
+ padding = cellPadding;
+ }
+
+ if (cellPadding !== padding) {
+ padding = null;
+ return false;
+ }
+ });
+
+ return padding || '';
+}
+
+
+
+
+
+
+
+