]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/utils/table-map.ts
Lexical: Added cell width fetching, Created custom row node
[bookstack] / resources / js / wysiwyg / utils / table-map.ts
1 import {CustomTableNode} from "../nodes/custom-table";
2 import {$isCustomTableCellNode, CustomTableCellNode} from "../nodes/custom-table-cell";
3 import {$isTableRowNode} from "@lexical/table";
4
5 export class TableMap {
6
7     rowCount: number = 0;
8     columnCount: number = 0;
9
10     // Represents an array (rows*columns in length) of cell nodes from top-left to
11     // bottom right. Cells may repeat where merged and covering multiple spaces.
12     cells: CustomTableCellNode[] = [];
13
14     constructor(table: CustomTableNode) {
15         this.buildCellMap(table);
16     }
17
18     protected buildCellMap(table: CustomTableNode) {
19         const rowsAndCells: CustomTableCellNode[][] = [];
20         const setCell = (x: number, y: number, cell: CustomTableCellNode) => {
21             if (typeof rowsAndCells[y] === 'undefined') {
22                 rowsAndCells[y] = [];
23             }
24
25             rowsAndCells[y][x] = cell;
26         };
27         const cellFilled = (x: number, y: number): boolean => !!(rowsAndCells[y] && rowsAndCells[y][x]);
28
29         const rowNodes = table.getChildren().filter(r => $isTableRowNode(r));
30         for (let rowIndex = 0; rowIndex < rowNodes.length; rowIndex++) {
31             const rowNode = rowNodes[rowIndex];
32             const cellNodes = rowNode.getChildren().filter(c => $isCustomTableCellNode(c));
33             let targetColIndex: number = 0;
34             for (let cellIndex = 0; cellIndex < cellNodes.length; cellIndex++) {
35                 const cellNode = cellNodes[cellIndex];
36                 const colspan = cellNode.getColSpan() || 1;
37                 const rowSpan = cellNode.getRowSpan() || 1;
38                 for (let x = targetColIndex; x < targetColIndex + colspan; x++) {
39                     for (let y = rowIndex; y < rowIndex + rowSpan; y++) {
40                         while (cellFilled(x, y)) {
41                             targetColIndex += 1;
42                             x += 1;
43                         }
44
45                         setCell(x, y, cellNode);
46                     }
47                 }
48                 targetColIndex += colspan;
49             }
50         }
51
52         this.rowCount = rowsAndCells.length;
53         this.columnCount = Math.max(...rowsAndCells.map(r => r.length));
54
55         const cells = [];
56         let lastCell: CustomTableCellNode = rowsAndCells[0][0];
57         for (let y = 0; y < this.rowCount; y++) {
58             for (let x = 0; x < this.columnCount; x++) {
59                 if (!rowsAndCells[y] || !rowsAndCells[y][x]) {
60                     cells.push(lastCell);
61                 } else {
62                     cells.push(rowsAndCells[y][x]);
63                     lastCell = rowsAndCells[y][x];
64                 }
65             }
66         }
67
68         this.cells = cells;
69     }
70
71     public getCellAtPosition(x: number, y: number): CustomTableCellNode {
72         const position = (y * this.columnCount) + x;
73         if (position >= this.cells.length) {
74             throw new Error(`TableMap Error: Attempted to get cell ${position+1} of ${this.cells.length}`);
75         }
76
77         return this.cells[position];
78     }
79
80     public getCellsInRange(fromX: number, fromY: number, toX: number, toY: number): CustomTableCellNode[] {
81         const minX = Math.max(Math.min(fromX, toX), 0);
82         const maxX = Math.min(Math.max(fromX, toX), this.columnCount - 1);
83         const minY = Math.max(Math.min(fromY, toY), 0);
84         const maxY = Math.min(Math.max(fromY, toY), this.rowCount - 1);
85
86         const cells = new Set<CustomTableCellNode>();
87
88         for (let y = minY; y <= maxY; y++) {
89             for (let x = minX; x <= maxX; x++) {
90                 cells.add(this.getCellAtPosition(x, y));
91             }
92         }
93
94         return [...cells.values()];
95     }
96 }