extend table and table cell support for cell merge, span, borders ... (#6)
* extend table and table cell support for cell merge, span, borders ... * added tests for table cell borders and table cell width, renamed some variables, added jsdoc
This commit is contained in:
@ -1 +1,2 @@
|
|||||||
export * from "./table";
|
export * from "./table";
|
||||||
|
export * from './table-cell';
|
181
src/file/table/table-cell.spec.ts
Normal file
181
src/file/table/table-cell.spec.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { TableCellBorders, BorderStyle, TableCellWidth, WidthType } from "./table-cell";
|
||||||
|
import { Formatter } from "../../export/formatter";
|
||||||
|
|
||||||
|
describe("TableCellBorders", () => {
|
||||||
|
describe("#prepForXml", () => {
|
||||||
|
it("should not add empty borders element if there are no borders defined", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
197
src/file/table/table-cell.ts
Normal file
197
src/file/table/table-cell.ts
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import { XmlComponent, XmlAttributeComponent, IXmlableObject } from "file/xml-components";
|
||||||
|
|
||||||
|
export enum BorderStyle {
|
||||||
|
SINGLE = "single",
|
||||||
|
DASH_DOT_STROKED = "dashDotStroked",
|
||||||
|
DASHED = "dashed",
|
||||||
|
DASH_SMALL_GAP = "dashSmallGap",
|
||||||
|
DOT_DASH = "dotDash",
|
||||||
|
DOT_DOT_DASH = "dotDotDash",
|
||||||
|
DOTTED = "dotted",
|
||||||
|
DOUBLE = "double",
|
||||||
|
DOUBLE_WAVE = "doubleWave",
|
||||||
|
INSET = "inset",
|
||||||
|
NIL = "nil",
|
||||||
|
NONE = "none",
|
||||||
|
OUTSET = "outset",
|
||||||
|
THICK = "thick",
|
||||||
|
THICK_THIN_LARGE_GAP = "thickThinLargeGap",
|
||||||
|
THICK_THIN_MEDIUM_GAP = "thickThinMediumGap",
|
||||||
|
THICK_THIN_SMALL_GAP = "thickThinSmallGap",
|
||||||
|
THIN_THICK_LARGE_GAP = "thinThickLargeGap",
|
||||||
|
THIN_THICK_MEDIUM_GAP = "thinThickMediumGap",
|
||||||
|
THIN_THICK_SMALL_GAP = "thinThickSmallGap",
|
||||||
|
THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap",
|
||||||
|
THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap",
|
||||||
|
THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap",
|
||||||
|
THREE_D_EMBOSS = "threeDEmboss",
|
||||||
|
THREE_D_ENGRAVE = "threeDEngrave",
|
||||||
|
TRIPLE = "triple",
|
||||||
|
WAVE = "wave",
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
setProperties(style: BorderStyle, size: number, color: string) {
|
||||||
|
let attrs = new CellBorderAttributes({
|
||||||
|
style: style,
|
||||||
|
size: size,
|
||||||
|
color: color,
|
||||||
|
});
|
||||||
|
this.root.push(attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TableCellBorders extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:tcBorders");
|
||||||
|
}
|
||||||
|
|
||||||
|
public prepForXml(): IXmlableObject {
|
||||||
|
return this.root.length > 0 ? super.prepForXml() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
addTopBorder(style: BorderStyle, size: number, color: string) {
|
||||||
|
const top = new BaseTableCellBorder("w:top");
|
||||||
|
top.setProperties(style, size, color);
|
||||||
|
this.root.push(top);
|
||||||
|
}
|
||||||
|
|
||||||
|
addStartBorder(style: BorderStyle, size: number, color: string) {
|
||||||
|
const start = new BaseTableCellBorder("w:start");
|
||||||
|
start.setProperties(style, size, color);
|
||||||
|
this.root.push(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
addBottomBorder(style: BorderStyle, size: number, color: string) {
|
||||||
|
const bottom = new BaseTableCellBorder("w:bottom");
|
||||||
|
bottom.setProperties(style, size, color);
|
||||||
|
this.root.push(bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
addEndBorder(style: BorderStyle, size: number, color: string) {
|
||||||
|
const end = new BaseTableCellBorder("w:end");
|
||||||
|
end.setProperties(style, size, color);
|
||||||
|
this.root.push(end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { IXmlableObject, XmlComponent } from "file/xml-components";
|
|||||||
import { Paragraph } from "../paragraph";
|
import { Paragraph } from "../paragraph";
|
||||||
import { TableGrid } from "./grid";
|
import { TableGrid } from "./grid";
|
||||||
import { TableProperties, WidthTypes } from "./properties";
|
import { TableProperties, WidthTypes } from "./properties";
|
||||||
|
import { TableCellBorders, GridSpan, VMerge, VMergeType, VerticalAlign, VAlign, TableCellWidth, WidthType } from "file/table/table-cell";
|
||||||
|
|
||||||
export class Table extends XmlComponent {
|
export class Table extends XmlComponent {
|
||||||
private readonly properties: TableProperties;
|
private readonly properties: TableProperties;
|
||||||
@ -123,7 +124,29 @@ export class TableCell extends XmlComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TableCellProperties extends XmlComponent {
|
export class TableCellProperties extends XmlComponent {
|
||||||
|
private cellBorder: TableCellBorders;
|
||||||
constructor() {
|
constructor() {
|
||||||
super("w:tcPr");
|
super("w:tcPr");
|
||||||
|
this.cellBorder = new TableCellBorders();
|
||||||
|
this.root.push(this.cellBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
get borders() {
|
||||||
|
return this.cellBorder;
|
||||||
|
}
|
||||||
|
addGridSpan(cellSpan: number) {
|
||||||
|
this.root.push(new GridSpan(cellSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
addVerticalMerge(type: VMergeType) {
|
||||||
|
this.root.push(new VMerge(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
setVerticalAlign(vAlignType: VerticalAlign) {
|
||||||
|
this.root.push(new VAlign(vAlignType));
|
||||||
|
}
|
||||||
|
|
||||||
|
setWidth(width: string | number, type: WidthType) {
|
||||||
|
this.root.push(new TableCellWidth(width, type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user