Refactor row merging to table level

This commit is contained in:
Dolan Miu
2019-09-25 00:57:24 +01:00
parent cc36ea7542
commit 7aa4134e2b
5 changed files with 86 additions and 42 deletions

View File

@ -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 from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build"; import { Document, HeadingLevel, Packer, Paragraph, ShadingType, Table, TableCell, TableRow, WidthType } from "../build";
@ -125,13 +125,61 @@ const table3 = new Table({
const table4 = new Table({ const table4 = new Table({
rows: [ 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({ new TableRow({
children: [ children: [
new TableCell({ new TableCell({
children: [], children: [],
}), }),
new TableCell({ new TableCell({
children: [], children: [new Paragraph("1,2")],
rowSpan: 2,
}), }),
], ],
}), }),
@ -163,8 +211,10 @@ doc.addSection({
heading: HeadingLevel.HEADING_2, heading: HeadingLevel.HEADING_2,
}), }),
table3, table3,
new Paragraph("hi"), new Paragraph("Merging columns"),
table4, table4,
new Paragraph("More Merging columns"),
table5,
], ],
}); });

View File

@ -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 from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build"; import { Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";

View File

@ -5,7 +5,6 @@ import { IXmlableObject, XmlComponent } from "file/xml-components";
import { ITableShadingAttributesProperties } from "../shading"; import { ITableShadingAttributesProperties } from "../shading";
import { Table } from "../table"; import { Table } from "../table";
import { TableRow } from "../table-row";
import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins"; import { ITableCellMarginOptions } from "./cell-margin/table-cell-margins";
import { VerticalAlign, VMergeType } from "./table-cell-components"; import { VerticalAlign, VMergeType } from "./table-cell-components";
import { TableCellProperties } from "./table-cell-properties"; import { TableCellProperties } from "./table-cell-properties";
@ -42,15 +41,8 @@ export interface ITableCellOptions {
readonly children: Array<Paragraph | Table>; readonly children: Array<Paragraph | Table>;
} }
interface ITableCellMetaData {
readonly column: TableCell[];
readonly row: TableRow;
}
export class TableCell extends XmlComponent { export class TableCell extends XmlComponent {
private readonly properties: TableCellProperties; private readonly properties: TableCellProperties;
// tslint:disable-next-line: readonly-keyword
private metaData: ITableCellMetaData;
constructor(readonly options: ITableCellOptions) { constructor(readonly options: ITableCellOptions) {
super("w:tc"); super("w:tc");
@ -82,6 +74,10 @@ export class TableCell extends XmlComponent {
this.properties.addGridSpan(options.columnSpan); this.properties.addGridSpan(options.columnSpan);
} }
if (options.rowSpan && options.rowSpan > 1) {
this.properties.addVerticalMerge(VMergeType.RESTART);
}
if (options.borders) { if (options.borders) {
if (options.borders.top) { if (options.borders.top) {
this.properties.Borders.addTopBorder(options.borders.top.style, options.borders.top.size, options.borders.top.color); 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 { 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 // Cells must end with a paragraph
if (!(this.root[this.root.length - 1] instanceof Paragraph)) { if (!(this.root[this.root.length - 1] instanceof Paragraph)) {
this.root.push(new Paragraph({})); this.root.push(new Paragraph({}));
} }
return super.prepForXml(); return super.prepForXml();
} }
public set MetaData(metaData: ITableCellMetaData) {
this.metaData = metaData;
}
} }

View File

@ -45,4 +45,9 @@ export class TableRow extends XmlComponent {
public get Children(): TableCell[] { public get Children(): TableCell[] {
return this.options.children; 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);
}
} }

View File

@ -1,7 +1,7 @@
// http://officeopenxml.com/WPtableGrid.php // http://officeopenxml.com/WPtableGrid.php
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { TableGrid } from "./grid"; import { TableGrid } from "./grid";
import { WidthType } from "./table-cell"; import { TableCell, VMergeType, WidthType } from "./table-cell";
import { ITableFloatOptions, TableProperties } from "./table-properties"; import { ITableFloatOptions, TableProperties } from "./table-properties";
import { TableLayoutType } from "./table-properties/table-layout"; import { TableLayoutType } from "./table-properties/table-layout";
import { TableRow } from "./table-row"; import { TableRow } from "./table-row";
@ -57,16 +57,31 @@ export class Table extends XmlComponent {
this.root.push(new TableGrid(columnWidths)); this.root.push(new TableGrid(columnWidths));
for (const row of rows) { 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); 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) { if (float) {
this.properties.setTableFloatProperties(float); this.properties.setTableFloatProperties(float);
} }