]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/utils/tables.ts
respective book and chapter structure added.
[bookstack] / resources / js / wysiwyg / utils / tables.ts
index 959c8a4236b2e6787ac1367b7c1c83d1b822bfb4..aa8ec89ba7770e18a7f655d1378ba8043d0a1c79 100644 (file)
@@ -1,10 +1,12 @@
 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;
@@ -123,6 +125,17 @@ export function $setTableCellColumnWidth(cell: CustomTableCellNode, width: strin
     }
 }
 
+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();
@@ -131,4 +144,174 @@ export function $getTableCellsFromSelection(selection: BaseSelection|null): Cust
 
     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 || '';
+}
+
+
+
+
+
+
+
+