Add declarative column merge
This commit is contained in:
@ -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";
|
||||
|
@ -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) => {
|
||||
|
@ -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],
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 }],
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
});
|
@ -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];
|
||||
// }
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user