Tidied up table components
This commit is contained in:
2
src/file/table/table-cell/index.ts
Normal file
2
src/file/table/table-cell/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./table-cell";
|
||||
export * from "./table-cell-components";
|
204
src/file/table/table-cell/table-cell-components.ts
Normal file
204
src/file/table/table-cell/table-cell-components.ts
Normal file
@ -0,0 +1,204 @@
|
||||
import { BorderStyle } from "file/styles";
|
||||
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
interface ICellBorder {
|
||||
style: BorderStyle;
|
||||
size: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
class CellBorderAttributes extends XmlAttributeComponent<ICellBorder> {
|
||||
protected xmlKeys = { style: "w:val", size: "w:sz", color: "w:color" };
|
||||
}
|
||||
|
||||
class BaseTableCellBorder extends XmlComponent {
|
||||
public setProperties(style: BorderStyle, size: number, color: string): BaseTableCellBorder {
|
||||
const attrs = new CellBorderAttributes({
|
||||
style: style,
|
||||
size: size,
|
||||
color: color,
|
||||
});
|
||||
this.root.push(attrs);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class TableCellBorders extends XmlComponent {
|
||||
constructor() {
|
||||
super("w:tcBorders");
|
||||
}
|
||||
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
if (this.root.length > 0) {
|
||||
return super.prepForXml();
|
||||
}
|
||||
}
|
||||
|
||||
public addTopBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||
const top = new BaseTableCellBorder("w:top");
|
||||
top.setProperties(style, size, color);
|
||||
this.root.push(top);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public addStartBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||
const start = new BaseTableCellBorder("w:start");
|
||||
start.setProperties(style, size, color);
|
||||
this.root.push(start);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public addBottomBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||
const bottom = new BaseTableCellBorder("w:bottom");
|
||||
bottom.setProperties(style, size, color);
|
||||
this.root.push(bottom);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public addEndBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||
const end = new BaseTableCellBorder("w:end");
|
||||
end.setProperties(style, size, color);
|
||||
this.root.push(end);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attributes fot the GridSpan element.
|
||||
*/
|
||||
class GridSpanAttributes extends XmlAttributeComponent<{ val: number }> {
|
||||
protected xmlKeys = { val: "w:val" };
|
||||
}
|
||||
|
||||
/**
|
||||
* GridSpan element. Should be used in a table cell. Pass the number of columns that this cell need to span.
|
||||
*/
|
||||
export class GridSpan extends XmlComponent {
|
||||
constructor(value: number) {
|
||||
super("w:gridSpan");
|
||||
|
||||
this.root.push(
|
||||
new GridSpanAttributes({
|
||||
val: value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical merge types.
|
||||
*/
|
||||
export enum VMergeType {
|
||||
/**
|
||||
* Cell that is merged with upper one.
|
||||
*/
|
||||
CONTINUE = "continue",
|
||||
/**
|
||||
* Cell that is starting the vertical merge.
|
||||
*/
|
||||
RESTART = "restart",
|
||||
}
|
||||
|
||||
class VMergeAttributes extends XmlAttributeComponent<{ val: VMergeType }> {
|
||||
protected xmlKeys = { val: "w:val" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical merge element. Should be used in a table cell.
|
||||
*/
|
||||
export class VMerge extends XmlComponent {
|
||||
constructor(value: VMergeType) {
|
||||
super("w:vMerge");
|
||||
|
||||
this.root.push(
|
||||
new VMergeAttributes({
|
||||
val: value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export enum VerticalAlign {
|
||||
BOTTOM = "bottom",
|
||||
CENTER = "center",
|
||||
TOP = "top",
|
||||
}
|
||||
|
||||
class VAlignAttributes extends XmlAttributeComponent<{ val: VerticalAlign }> {
|
||||
protected xmlKeys = { val: "w:val" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Vertical align element.
|
||||
*/
|
||||
export class VAlign extends XmlComponent {
|
||||
constructor(value: VerticalAlign) {
|
||||
super("w:vAlign");
|
||||
|
||||
this.root.push(
|
||||
new VAlignAttributes({
|
||||
val: value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export enum WidthType {
|
||||
/** Auto. */
|
||||
AUTO = "auto",
|
||||
/** Value is in twentieths of a point */
|
||||
DXA = "dxa",
|
||||
/** No (empty) value. */
|
||||
NIL = "nil",
|
||||
/** Value is in percentage. */
|
||||
PERCENTAGE = "pct",
|
||||
}
|
||||
|
||||
class TableCellWidthAttributes extends XmlAttributeComponent<{ type: WidthType; width: string | number }> {
|
||||
protected xmlKeys = { width: "w:w", type: "w:type" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Table cell width element.
|
||||
*/
|
||||
export class TableCellWidth extends XmlComponent {
|
||||
constructor(value: string | number, type: WidthType) {
|
||||
super("w:tcW");
|
||||
|
||||
this.root.push(
|
||||
new TableCellWidthAttributes({
|
||||
width: value,
|
||||
type: type,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface ITableCellShadingAttributesProperties {
|
||||
fill?: string;
|
||||
color?: string;
|
||||
val?: string;
|
||||
}
|
||||
|
||||
class TableCellShadingAttributes extends XmlAttributeComponent<ITableCellShadingAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
fill: "w:fill",
|
||||
color: "w:color",
|
||||
val: "w:val",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Table cell shading element.
|
||||
*/
|
||||
export class TableCellShading extends XmlComponent {
|
||||
constructor(attrs: object) {
|
||||
super("w:shd");
|
||||
this.root.push(new TableCellShadingAttributes(attrs));
|
||||
}
|
||||
}
|
57
src/file/table/table-cell/table-cell-properties.ts
Normal file
57
src/file/table/table-cell/table-cell-properties.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { XmlComponent } from "file/xml-components";
|
||||
|
||||
import {
|
||||
GridSpan,
|
||||
TableCellBorders,
|
||||
TableCellShading,
|
||||
TableCellWidth,
|
||||
VAlign,
|
||||
VerticalAlign,
|
||||
VMerge,
|
||||
VMergeType,
|
||||
WidthType,
|
||||
} from "./table-cell-components";
|
||||
|
||||
export class TableCellProperties extends XmlComponent {
|
||||
private readonly cellBorder: TableCellBorders;
|
||||
|
||||
constructor() {
|
||||
super("w:tcPr");
|
||||
this.cellBorder = new TableCellBorders();
|
||||
this.root.push(this.cellBorder);
|
||||
}
|
||||
|
||||
public get Borders(): TableCellBorders {
|
||||
return this.cellBorder;
|
||||
}
|
||||
|
||||
public addGridSpan(cellSpan: number): TableCellProperties {
|
||||
this.root.push(new GridSpan(cellSpan));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public addVerticalMerge(type: VMergeType): TableCellProperties {
|
||||
this.root.push(new VMerge(type));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setVerticalAlign(type: VerticalAlign): TableCellProperties {
|
||||
this.root.push(new VAlign(type));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setWidth(width: string | number, type: WidthType): TableCellProperties {
|
||||
this.root.push(new TableCellWidth(width, type));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public setShading(attrs: object): TableCellProperties {
|
||||
this.root.push(new TableCellShading(attrs));
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
181
src/file/table/table-cell/table-cell.spec.ts
Normal file
181
src/file/table/table-cell/table-cell.spec.ts
Normal file
@ -0,0 +1,181 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "../../../export/formatter";
|
||||
import { BorderStyle } from "../../styles";
|
||||
import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell-components";
|
||||
|
||||
describe("TableCellBorders", () => {
|
||||
describe("#prepForXml", () => {
|
||||
it("should not add empty borders element if there are no borders defined", () => {
|
||||
const tb = new TableCellBorders();
|
||||
expect(() => new Formatter().format(tb)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe("#addingBorders", () => {
|
||||
it("should add top border", () => {
|
||||
const tb = new TableCellBorders();
|
||||
tb.addTopBorder(BorderStyle.DOTTED, 1, "FF00FF");
|
||||
|
||||
const tree = new Formatter().format(tb);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcBorders": [
|
||||
{
|
||||
"w:top": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 1,
|
||||
"w:val": "dotted",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add start(left) border", () => {
|
||||
const tb = new TableCellBorders();
|
||||
tb.addStartBorder(BorderStyle.SINGLE, 2, "FF00FF");
|
||||
|
||||
const tree = new Formatter().format(tb);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcBorders": [
|
||||
{
|
||||
"w:start": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 2,
|
||||
"w:val": "single",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add bottom border", () => {
|
||||
const tb = new TableCellBorders();
|
||||
tb.addBottomBorder(BorderStyle.DOUBLE, 1, "FF00FF");
|
||||
|
||||
const tree = new Formatter().format(tb);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcBorders": [
|
||||
{
|
||||
"w:bottom": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 1,
|
||||
"w:val": "double",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add end(right) border", () => {
|
||||
const tb = new TableCellBorders();
|
||||
tb.addEndBorder(BorderStyle.THICK, 3, "FF00FF");
|
||||
|
||||
const tree = new Formatter().format(tb);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcBorders": [
|
||||
{
|
||||
"w:end": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 3,
|
||||
"w:val": "thick",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should add multiple borders", () => {
|
||||
const tb = new TableCellBorders();
|
||||
tb.addTopBorder(BorderStyle.DOTTED, 1, "FF00FF");
|
||||
tb.addEndBorder(BorderStyle.THICK, 3, "FF00FF");
|
||||
tb.addBottomBorder(BorderStyle.DOUBLE, 1, "FF00FF");
|
||||
tb.addStartBorder(BorderStyle.SINGLE, 2, "FF00FF");
|
||||
|
||||
const tree = new Formatter().format(tb);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcBorders": [
|
||||
{
|
||||
"w:top": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 1,
|
||||
"w:val": "dotted",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:end": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 3,
|
||||
"w:val": "thick",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:bottom": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 1,
|
||||
"w:val": "double",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:start": [
|
||||
{
|
||||
_attr: {
|
||||
"w:color": "FF00FF",
|
||||
"w:sz": 2,
|
||||
"w:val": "single",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("TableCellWidth", () => {
|
||||
describe("#constructor", () => {
|
||||
it("should create object", () => {
|
||||
const tcWidth = new TableCellWidth(100, WidthType.DXA);
|
||||
const tree = new Formatter().format(tcWidth);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:tcW": [
|
||||
{
|
||||
_attr: {
|
||||
"w:type": "dxa",
|
||||
"w:w": 100,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
45
src/file/table/table-cell/table-cell.ts
Normal file
45
src/file/table/table-cell/table-cell.ts
Normal file
@ -0,0 +1,45 @@
|
||||
// http://officeopenxml.com/WPtableGrid.php
|
||||
import { Paragraph } from "file/paragraph";
|
||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { Table } from "../table";
|
||||
import { TableCellProperties } from "./table-cell-properties";
|
||||
|
||||
export class TableCell extends XmlComponent {
|
||||
private readonly properties: TableCellProperties;
|
||||
|
||||
constructor() {
|
||||
super("w:tc");
|
||||
this.properties = new TableCellProperties();
|
||||
this.root.push(this.properties);
|
||||
}
|
||||
|
||||
public addContent(content: Paragraph | Table): TableCell {
|
||||
this.root.push(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
public prepForXml(): IXmlableObject | undefined {
|
||||
// Cells must end with a paragraph
|
||||
const retval = super.prepForXml();
|
||||
if (!retval) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const content = retval["w:tc"];
|
||||
if (!content[content.length - 1]["w:p"]) {
|
||||
content.push(new Paragraph().prepForXml());
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
public createParagraph(text?: string): Paragraph {
|
||||
const para = new Paragraph(text);
|
||||
this.addContent(para);
|
||||
return para;
|
||||
}
|
||||
|
||||
public get CellProperties(): TableCellProperties {
|
||||
return this.properties;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user