Add declarative column merge

This commit is contained in:
Dolan Miu
2019-09-22 02:39:38 +01:00
parent d2f82052b4
commit a9d4ebc898
8 changed files with 179 additions and 108 deletions

View File

@ -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";

View File

@ -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) => {

View File

@ -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],
});

View File

@ -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<Paragraph | Table>;
}
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;
}
}

View File

@ -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 }],
// });
// });
// });
});

View File

@ -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];
// }
}

View File

@ -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;
}
}

View File

@ -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);
}