PluginProbe
Tableberg – Simple Gutenberg Table Block / 1.1.5
Tableberg – Simple Gutenberg Table Block v1.1.5
1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.5 1.0.4 1.0.3 1.0.2 1.0.1 trunk 0.0.2 0.2.1 0.3.2 0.3.3 0.4.1 0.5.0 0.5.1 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 All 42 releases
tableberg / src / merge.ts

merge.ts in Tableberg – Simple Gutenberg Table Block 1.1.5, at src/merge.ts

103 lines 2.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { Cell, CellKey, parseCellKey } from "./attributes";
2 import { TableState } from "./store";
3
4 function getSpan(cells: Record<CellKey, Cell>, coord: CellKey) {
5 return (
6 cells[coord]?.span || {
7 rowSpan: 1,
8 colSpan: 1,
9 }
10 );
11 }
12
13 export function areAllMergeable(
14 coords: Array<CellKey>,
15 cells: Record<CellKey, Cell>
16 ): boolean {
17 if (coords.length < 2) return false;
18
19 const occupiedGrid = new Set<string>();
20 for (const coord of coords) {
21 const [r, c] = parseCellKey(coord);
22 const span = getSpan(cells, coord);
23 for (let rs = 0; rs < span.rowSpan; rs++) {
24 for (let cs = 0; cs < span.colSpan; cs++) {
25 occupiedGrid.add(`${r + rs},${c + cs}`);
26 }
27 }
28 }
29
30 let minRow = Infinity,
31 maxRow = -Infinity;
32 let minCol = Infinity,
33 maxCol = -Infinity;
34
35 for (const key of occupiedGrid) {
36 const [r, c] = key.split(",").map(Number);
37 minRow = Math.min(minRow, r);
38 maxRow = Math.max(maxRow, r);
39 minCol = Math.min(minCol, c);
40 maxCol = Math.max(maxCol, c);
41 }
42
43 for (let r = minRow; r <= maxRow; r++) {
44 for (let c = minCol; c <= maxCol; c++) {
45 if (!occupiedGrid.has(`${r},${c}`)) {
46 return false;
47 }
48 }
49 }
50
51 return true;
52 }
53
54 export function mergeCells(coords: Array<CellKey>, state: TableState): void {
55 if (coords.length < 2) return;
56
57 if (!areAllMergeable(coords, state.cells)) {
58 console.warn("Cells are not mergeable");
59 return;
60 }
61
62 const sorted = [...coords].sort((a, b) => {
63 const [aRow, aCol] = parseCellKey(a);
64 const [bRow, bCol] = parseCellKey(b);
65 return aRow === bRow ? aCol - bCol : aRow - bRow;
66 });
67 const topLeft = sorted[0];
68 const [topLeftRow, topLeftCol] = parseCellKey(topLeft);
69
70 let minRow = topLeftRow;
71 let maxRow = topLeftRow;
72 let minCol = topLeftCol;
73 let maxCol = topLeftCol;
74
75 for (const coord of coords) {
76 const [r, c] = parseCellKey(coord);
77 const span = getSpan(state.cells, coord);
78 minRow = Math.min(minRow, r);
79 maxRow = Math.max(maxRow, r + span.rowSpan - 1);
80 minCol = Math.min(minCol, c);
81 maxCol = Math.max(maxCol, c + span.colSpan - 1);
82 }
83
84 const newRowSpan = maxRow - minRow + 1;
85 const newColSpan = maxCol - minCol + 1;
86 const coordSet = new Set(coords);
87 const nextCells = { ...state.cells };
88
89 Object.keys(nextCells).forEach(key => {
90 if (coordSet.has(key as CellKey) && key !== topLeft) {
91 delete nextCells[key as CellKey];
92 }
93 });
94
95 nextCells[topLeft] = {
96 ...(nextCells[topLeft] || {}),
97 span: { rowSpan: newRowSpan, colSpan: newColSpan },
98 };
99
100 state.setCells(nextCells);
101 state.setSelectedCells([topLeft]);
102 }
103