diff --git a/demo/32-merge-table-cells.ts b/demo/32-merge-table-cells.ts index 55a9181281..873f3b50db 100644 --- a/demo/32-merge-table-cells.ts +++ b/demo/32-merge-table-cells.ts @@ -1,4 +1,4 @@ -// Example of how you would merge cells together +// Example of how you would merge cells together - Rows and Columns // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; diff --git a/demo/41-merge-table-cells-2.ts b/demo/41-merge-table-cells-2.ts index 50141a1a7a..3ef8615590 100644 --- a/demo/41-merge-table-cells-2.ts +++ b/demo/41-merge-table-cells-2.ts @@ -117,8 +117,143 @@ const table = new Table({ ], }); +const table2 = new Table({ + rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + }), + new TableCell({ + children: [new Paragraph("0,1")], + rowSpan: 2, + }), + new TableCell({ + children: [new Paragraph("0,2")], + }), + new TableCell({ + children: [new Paragraph("0,3")], + }), + new TableCell({ + children: [new Paragraph("0,4")], + }), + new TableCell({ + children: [new Paragraph("0,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("1,0")], + }), + new TableCell({ + children: [new Paragraph("1,2")], + }), + new TableCell({ + children: [new Paragraph("1,3")], + }), + new TableCell({ + children: [new Paragraph("1,4")], + }), + new TableCell({ + children: [new Paragraph("1,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("2,0")], + }), + new TableCell({ + children: [new Paragraph("2,1")], + }), + new TableCell({ + children: [new Paragraph("2,2")], + }), + new TableCell({ + children: [new Paragraph("2,3")], + }), + new TableCell({ + children: [new Paragraph("2,4")], + }), + new TableCell({ + children: [new Paragraph("2,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("3,0")], + }), + new TableCell({ + children: [new Paragraph("3,1")], + }), + new TableCell({ + children: [new Paragraph("3,2")], + }), + new TableCell({ + children: [new Paragraph("3,3")], + }), + new TableCell({ + children: [new Paragraph("3,4")], + }), + new TableCell({ + children: [new Paragraph("3,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("4,0")], + }), + new TableCell({ + children: [new Paragraph("4,1")], + }), + new TableCell({ + children: [new Paragraph("4,2")], + }), + new TableCell({ + children: [new Paragraph("4,3")], + }), + new TableCell({ + children: [new Paragraph("4,4")], + }), + new TableCell({ + children: [new Paragraph("4,5")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + new TableCell({ + children: [], + }), + ], + }), + ], +}); + doc.addSection({ - children: [table], + children: [table, new Paragraph(""), table2], }); Packer.toBuffer(doc).then((buffer) => { diff --git a/demo/43-images-to-table-cell-2.ts b/demo/43-images-to-table-cell-2.ts index 1840c9382c..ab950d284c 100644 --- a/demo/43-images-to-table-cell-2.ts +++ b/demo/43-images-to-table-cell-2.ts @@ -36,6 +36,7 @@ const table = new Table({ }), new TableCell({ children: [], + rowSpan: 2, }), ], }), @@ -74,8 +75,6 @@ const table = new Table({ ], }); -table.getColumn(3).mergeCells(1, 2); - doc.addSection({ children: [table], }); diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index 05416b34d5..a93652e5d6 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -5,6 +5,7 @@ import { IXmlableObject, XmlComponent } from "file/xml-components"; import { ITableShadingAttributesProperties } from "../shading"; import { Table } from "../table"; +import { TableRow } from "../table-row"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; import { VerticalAlign, VMergeType } from "./table-cell-components"; import { TableCellProperties } from "./table-cell-properties"; @@ -15,6 +16,7 @@ export interface ITableCellOptions { readonly verticalAlign?: VerticalAlign; readonly verticalMerge?: VMergeType; readonly columnSpan?: number; + readonly rowSpan?: number; readonly borders?: { readonly top?: { readonly style: BorderStyle; @@ -40,8 +42,15 @@ export interface ITableCellOptions { readonly children: Array; } +interface ITableCellMetaData { + readonly column: TableCell[]; + readonly row: TableRow; +} + export class TableCell extends XmlComponent { private readonly properties: TableCellProperties; + // tslint:disable-next-line: readonly-keyword + private metaData: ITableCellMetaData; constructor(readonly options: ITableCellOptions) { super("w:tc"); @@ -98,10 +107,32 @@ export class TableCell extends XmlComponent { } public prepForXml(): IXmlableObject | undefined { + // Row Span has to be added in this method and not the constructor because it needs to know information about the column which happens after Table Cell construction + // Row Span of 1 will crash word as it will add RESTART and not a corresponding CONTINUE + if (this.options.rowSpan && this.options.rowSpan > 1) { + this.properties.addVerticalMerge(VMergeType.RESTART); + + const currentIndex = this.metaData.column.indexOf(this); + for (let i = currentIndex + 1; i <= currentIndex + this.options.rowSpan - 1; i++) { + this.metaData.column[i].metaData.row.Children.splice( + i, + 0, + new TableCell({ + children: [], + }), + ); + this.metaData.column[i].properties.addVerticalMerge(VMergeType.CONTINUE); + } + } + // Cells must end with a paragraph if (!(this.root[this.root.length - 1] instanceof Paragraph)) { this.root.push(new Paragraph({})); } return super.prepForXml(); } + + public set MetaData(metaData: ITableCellMetaData) { + this.metaData = metaData; + } } diff --git a/src/file/table/table-column.spec.ts b/src/file/table/table-column.spec.ts deleted file mode 100644 index 50e9cb8aa4..0000000000 --- a/src/file/table/table-column.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { expect } from "chai"; - -// import { Formatter } from "export/formatter"; -// import { EMPTY_OBJECT } from "file/xml-components"; - -import { TableCell } from "./table-cell"; -import { TableColumn } from "./table-column"; - -describe("TableColumn", () => { - let cells: TableCell[]; - beforeEach(() => { - cells = [ - new TableCell({ - children: [], - }), - new TableCell({ - children: [], - }), - new TableCell({ - children: [], - }), - ]; - }); - - describe("#getCell", () => { - it("should get the correct cell", () => { - const tableColumn = new TableColumn(cells); - const cell = tableColumn.getCell(0); - - expect(cell).to.deep.equal(cells[0]); - - const cell2 = tableColumn.getCell(1); - - expect(cell2).to.deep.equal(cells[1]); - }); - - it("should throw an error if index is out of bounds", () => { - const tableColumn = new TableColumn(cells); - - expect(() => tableColumn.getCell(9)).to.throw(); - }); - }); - - // describe("#mergeCells", () => { - // it("should add vMerge to correct cells", () => { - // const tableColumn = new TableColumn(cells); - // tableColumn.mergeCells(0, 2); - - // const tree = new Formatter().format(cells[0]); - // expect(tree).to.deep.equal({ - // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "restart" } } }] }, { "w:p": EMPTY_OBJECT }], - // }); - - // const tree2 = new Formatter().format(cells[1]); - // expect(tree2).to.deep.equal({ - // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], - // }); - - // const tree3 = new Formatter().format(cells[2]); - // expect(tree3).to.deep.equal({ - // "w:tc": [{ "w:tcPr": [{ "w:vMerge": { _attr: { "w:val": "continue" } } }] }, { "w:p": EMPTY_OBJECT }], - // }); - // }); - // }); -}); diff --git a/src/file/table/table-column.ts b/src/file/table/table-column.ts deleted file mode 100644 index fc8c2cc240..0000000000 --- a/src/file/table/table-column.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TableCell } from "./table-cell"; - -export class TableColumn { - constructor(private readonly cells: TableCell[]) {} - - public getCell(index: number): TableCell { - const cell = this.cells[index]; - - if (!cell) { - throw Error("Index out of bounds when trying to get cell on column"); - } - - return cell; - } - - // public mergeCells(startIndex: number, endIndex: number): TableCell { - // this.cells[startIndex].addVerticalMerge(VMergeType.RESTART); - - // for (let i = startIndex + 1; i <= endIndex; i++) { - // this.cells[i].addVerticalMerge(VMergeType.CONTINUE); - // } - - // return this.cells[startIndex]; - // } -} diff --git a/src/file/table/table-row/table-row.ts b/src/file/table/table-row/table-row.ts index df47bf70bf..07cd811fb9 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -42,18 +42,7 @@ export class TableRow extends XmlComponent { return this.options.children.length; } - // public mergeCells(startIndex: number, endIndex: number): TableCell { - // const cellSpan = endIndex - startIndex + 1; - - // return this.addGridSpan(startIndex, cellSpan); - // } - - // private addGridSpan(index: number, cellSpan: number): TableCell { - // const remainCell = this.options.children[index]; - // remainCell.addGridSpan(cellSpan); - // this.options.children.splice(index + 1, cellSpan - 1); - // this.root.splice(index + 2, cellSpan - 1); - - // return remainCell; - // } + public get Children(): TableCell[] { + return this.options.children; + } } diff --git a/src/file/table/table.ts b/src/file/table/table.ts index ea8e64d72c..5459400aeb 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -57,6 +57,13 @@ export class Table extends XmlComponent { this.root.push(new TableGrid(columnWidths)); for (const row of rows) { + row.Children.forEach((cell, i) => { + cell.MetaData = { + column: rows.map((r) => r.Children[i]), + row: row, + }; + }); + this.root.push(row); }