| 1 |
import { BlockInstance } from "@wordpress/blocks"; |
| 2 |
|
| 3 |
import { |
| 4 |
Cell, |
| 5 |
CellElement, |
| 6 |
CellKey, |
| 7 |
RowConfigs, |
| 8 |
TablebergBlockAttrs, |
| 9 |
} from "../attributes"; |
| 10 |
|
| 11 |
/** |
| 12 |
* JS mirror of renderer/Table/InnerBlocksAttrsAdapter.php: reduces the v4 |
| 13 |
* native block tree (rows -> cells -> elements) to the v3 cells/rows shape. |
| 14 |
* Used to snapshot the tree into the table store so the existing preview UI |
| 15 |
* (sorting/search/pagination/responsive via PrimaryTable) renders from it. |
| 16 |
*/ |
| 17 |
export function blocksToV3Content(rowBlocks: BlockInstance[]): { |
| 18 |
cells: Record<CellKey, Cell>; |
| 19 |
rows: RowConfigs; |
| 20 |
rowsCount: number; |
| 21 |
colsCount: number; |
| 22 |
/** Grid position -> cell block clientId (for targeted write-backs). */ |
| 23 |
cellClientIds: Record<CellKey, string>; |
| 24 |
} { |
| 25 |
const cells: Record<CellKey, Cell> = {}; |
| 26 |
const cellClientIds: Record<CellKey, string> = {}; |
| 27 |
const rows: RowConfigs = []; |
| 28 |
const covered = new Set<string>(); |
| 29 |
let colsCount = 0; |
| 30 |
|
| 31 |
let row = 0; |
| 32 |
for (const rowBlock of rowBlocks) { |
| 33 |
if (rowBlock.name !== "tableberg/row") { |
| 34 |
continue; |
| 35 |
} |
| 36 |
|
| 37 |
const rowAttrs = rowBlock.attributes ?? {}; |
| 38 |
rows[row] = |
| 39 |
Object.keys(rowAttrs).length > 0 |
| 40 |
? ({ ...rowAttrs } as RowConfigs[number]) |
| 41 |
: null; |
| 42 |
|
| 43 |
let col = 0; |
| 44 |
for (const cellBlock of rowBlock.innerBlocks) { |
| 45 |
if (cellBlock.name !== "tableberg/cell") { |
| 46 |
continue; |
| 47 |
} |
| 48 |
|
| 49 |
while (covered.has(`${row},${col}`)) { |
| 50 |
col++; |
| 51 |
} |
| 52 |
|
| 53 |
const attrs = cellBlock.attributes ?? {}; |
| 54 |
// Keep the reverse adapter symmetric with v3-to-v4: all block |
| 55 |
// attributes pass through, while elements come from innerBlocks. |
| 56 |
const cell: Cell = { ...attrs } as Cell; |
| 57 |
cell.elements = blocksToElements(cellBlock.innerBlocks); |
| 58 |
|
| 59 |
cells[`${row},${col}` as CellKey] = cell; |
| 60 |
cellClientIds[`${row},${col}` as CellKey] = cellBlock.clientId; |
| 61 |
|
| 62 |
const rowSpan = Math.max(1, attrs.span?.rowSpan ?? 1); |
| 63 |
const colSpan = Math.max(1, attrs.span?.colSpan ?? 1); |
| 64 |
for (let dr = 0; dr < rowSpan; dr++) { |
| 65 |
for (let dc = 0; dc < colSpan; dc++) { |
| 66 |
if (dr === 0 && dc === 0) { |
| 67 |
continue; |
| 68 |
} |
| 69 |
covered.add(`${row + dr},${col + dc}`); |
| 70 |
} |
| 71 |
} |
| 72 |
|
| 73 |
col += colSpan; |
| 74 |
if (col > colsCount) { |
| 75 |
colsCount = col; |
| 76 |
} |
| 77 |
} |
| 78 |
|
| 79 |
while (covered.has(`${row},${col}`)) { |
| 80 |
col++; |
| 81 |
if (col > colsCount) { |
| 82 |
colsCount = col; |
| 83 |
} |
| 84 |
} |
| 85 |
|
| 86 |
row++; |
| 87 |
} |
| 88 |
|
| 89 |
return { cells, rows, rowsCount: row, colsCount, cellClientIds }; |
| 90 |
} |
| 91 |
|
| 92 |
function blocksToElements(elementBlocks: BlockInstance[]): CellElement[] { |
| 93 |
const elements: CellElement[] = []; |
| 94 |
|
| 95 |
for (const block of elementBlocks) { |
| 96 |
if (!block.name.startsWith("tableberg/")) { |
| 97 |
continue; |
| 98 |
} |
| 99 |
|
| 100 |
const name = block.name.slice("tableberg/".length); |
| 101 |
const { bindings, ...attributes } = block.attributes ?? {}; |
| 102 |
|
| 103 |
const element = { |
| 104 |
name, |
| 105 |
attributes, |
| 106 |
} as CellElement; |
| 107 |
if (bindings && Object.keys(bindings).length > 0) { |
| 108 |
element.bindings = bindings; |
| 109 |
} |
| 110 |
|
| 111 |
elements.push(element); |
| 112 |
} |
| 113 |
|
| 114 |
return elements; |
| 115 |
} |
| 116 |
|