diff --git a/demo/32-merge-table-cells.ts b/demo/32-merge-and-shade-table-cells.ts similarity index 74% rename from demo/32-merge-table-cells.ts rename to demo/32-merge-and-shade-table-cells.ts index 873f3b50db..1112931079 100644 --- a/demo/32-merge-table-cells.ts +++ b/demo/32-merge-and-shade-table-cells.ts @@ -1,4 +1,4 @@ -// Example of how you would merge cells together - Rows and Columns +// Example of how you would merge cells together (Rows and Columns) and apply shading // 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"; @@ -125,13 +125,61 @@ const table3 = new Table({ const table4 = new Table({ rows: [ + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("0,0")], + columnSpan: 2, + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("1,0")], + }), + new TableCell({ + children: [new Paragraph("1,1")], + }), + ], + }), + new TableRow({ + children: [ + new TableCell({ + children: [new Paragraph("2,0")], + columnSpan: 2, + }), + ], + }), + ], + width: 100, + widthUnitType: WidthType.PERCENTAGE, +}); + +const table5 = 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 TableRow({ children: [ new TableCell({ children: [], }), new TableCell({ - children: [], + children: [new Paragraph("1,2")], + rowSpan: 2, }), ], }), @@ -163,8 +211,10 @@ doc.addSection({ heading: HeadingLevel.HEADING_2, }), table3, - new Paragraph("hi"), + new Paragraph("Merging columns"), table4, + new Paragraph("More Merging columns"), + table5, ], }); diff --git a/demo/41-merge-table-cells-2.ts b/demo/41-merge-table-cells-2.ts index 3ef8615590..94e7bffe1d 100644 --- a/demo/41-merge-table-cells-2.ts +++ b/demo/41-merge-table-cells-2.ts @@ -1,4 +1,4 @@ -// Multiple cells merging in the same table +// Multiple cells merging in the same table - Rows and Columns // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; diff --git a/src/file/table/table-cell/table-cell.ts b/src/file/table/table-cell/table-cell.ts index a93652e5d6..616065a0c1 100644 --- a/src/file/table/table-cell/table-cell.ts +++ b/src/file/table/table-cell/table-cell.ts @@ -5,7 +5,6 @@ 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"; @@ -42,15 +41,8 @@ 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"); @@ -82,6 +74,10 @@ export class TableCell extends XmlComponent { this.properties.addGridSpan(options.columnSpan); } + if (options.rowSpan && options.rowSpan > 1) { + this.properties.addVerticalMerge(VMergeType.RESTART); + } + if (options.borders) { if (options.borders.top) { this.properties.Borders.addTopBorder(options.borders.top.style, options.borders.top.size, options.borders.top.color); @@ -107,32 +103,10 @@ 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-row/table-row.ts b/src/file/table/table-row/table-row.ts index 07cd811fb9..466b7eb320 100644 --- a/src/file/table/table-row/table-row.ts +++ b/src/file/table/table-row/table-row.ts @@ -45,4 +45,9 @@ export class TableRow extends XmlComponent { public get Children(): TableCell[] { return this.options.children; } + + public addCellToIndex(cell: TableCell, index: number): void { + // Offset because properties is also in root. + this.root.splice(index + 1, 0, cell); + } } diff --git a/src/file/table/table.ts b/src/file/table/table.ts index 5459400aeb..64e276f73d 100644 --- a/src/file/table/table.ts +++ b/src/file/table/table.ts @@ -1,7 +1,7 @@ // http://officeopenxml.com/WPtableGrid.php import { XmlComponent } from "file/xml-components"; import { TableGrid } from "./grid"; -import { WidthType } from "./table-cell"; +import { TableCell, VMergeType, WidthType } from "./table-cell"; import { ITableFloatOptions, TableProperties } from "./table-properties"; import { TableLayoutType } from "./table-properties/table-layout"; import { TableRow } from "./table-row"; @@ -57,16 +57,31 @@ 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); } + for (const row of rows) { + row.Children.forEach((cell, cellIndex) => { + const column = rows.map((r) => r.Children[cellIndex]); + // 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 (cell.options.rowSpan && cell.options.rowSpan > 1) { + const thisCellsColumnIndex = column.indexOf(cell); + const endColumnIndex = thisCellsColumnIndex + (cell.options.rowSpan - 1); + + for (let i = thisCellsColumnIndex + 1; i <= endColumnIndex; i++) { + rows[i].addCellToIndex( + new TableCell({ + children: [], + verticalMerge: VMergeType.CONTINUE, + }), + i, + ); + } + } + }); + } + if (float) { this.properties.setTableFloatProperties(float); }