Merge pull request #454 from dolanmiu/feat/table-borders

Add table borders
This commit is contained in:
Dolan
2019-11-19 22:46:46 +00:00
committed by GitHub
7 changed files with 711 additions and 22 deletions

37
demo/49-table-borders.ts Normal file
View File

@ -0,0 +1,37 @@
// Add custom borders to the table itself
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { BorderStyle, Document, Packer, Paragraph, Table, TableCell, TableRow } from "../build";
const doc = new Document();
const table = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Hello")],
}),
new TableCell({
children: [],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("World")],
}),
],
}),
],
});
doc.addSection({ children: [table] });
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

33
package-lock.json generated
View File

@ -1098,7 +1098,7 @@
},
"browserify-aes": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
"dev": true,
"requires": {
@ -1135,7 +1135,7 @@
},
"browserify-rsa": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
"dev": true,
"requires": {
@ -1169,7 +1169,7 @@
},
"buffer": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
"dev": true,
"requires": {
@ -1642,7 +1642,7 @@
},
"create-hash": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
"dev": true,
"requires": {
@ -1655,7 +1655,7 @@
},
"create-hmac": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
"dev": true,
"requires": {
@ -1894,7 +1894,7 @@
},
"diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
"dev": true,
"requires": {
@ -3921,7 +3921,7 @@
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
"dev": true
},
"is-ci": {
@ -4345,7 +4345,7 @@
},
"jsesc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
"dev": true
},
@ -4771,7 +4771,7 @@
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
@ -4935,6 +4935,11 @@
"dev": true,
"optional": true
},
"nanoid": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.6.tgz",
"integrity": "sha512-2NDzpiuEy3+H0AVtdt8LoFi7PnqkOnIzYmJQp7xsEU6VexLluHQwKREuiz57XaQC5006seIadPrIZJhyS2n7aw=="
},
"nanomatch": {
"version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@ -6600,7 +6605,7 @@
},
"sha.js": {
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"dev": true,
"requires": {
@ -6634,6 +6639,14 @@
"rechoir": "^0.6.2"
}
},
"shortid": {
"version": "2.2.15",
"resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.15.tgz",
"integrity": "sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==",
"requires": {
"nanoid": "^2.1.0"
}
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",

View File

@ -1,3 +1,4 @@
export * from "./table-properties";
export * from "./table-float-properties";
export * from "./table-layout";
export * from "./table-borders";

View File

@ -0,0 +1,550 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/styles";
import { TableBorders } from "./table-borders";
describe("TableBorders", () => {
describe("#constructor", () => {
describe("default borders", () => {
it("should add a table cell top border using default width type", () => {
const tableBorders = new TableBorders({});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:left": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:right": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
],
});
});
});
describe("top border", () => {
it("should add a table cell top border", () => {
const tableBorders = new TableBorders({
top: {
style: BorderStyle.DOUBLE,
size: 1,
color: "red",
},
});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "red",
"w:space": 0,
"w:sz": 1,
"w:val": "double",
},
},
},
{
"w:left": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:right": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
],
});
});
});
describe("left border", () => {
it("should add a table cell left border", () => {
const tableBorders = new TableBorders({
left: {
style: BorderStyle.DOUBLE,
size: 1,
color: "red",
},
});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:left": {
_attr: {
"w:color": "red",
"w:space": 0,
"w:sz": 1,
"w:val": "double",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:right": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
],
});
});
});
describe("bottom border", () => {
it("should add a table cell bottom border", () => {
const tableBorders = new TableBorders({
bottom: {
style: BorderStyle.DOUBLE,
size: 1,
color: "red",
},
});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:left": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "red",
"w:space": 0,
"w:sz": 1,
"w:val": "double",
},
},
},
{
"w:right": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
],
});
});
});
describe("right border", () => {
it("should add a table cell right border", () => {
const tableBorders = new TableBorders({
right: {
style: BorderStyle.DOUBLE,
size: 1,
color: "red",
},
});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:left": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:right": {
_attr: {
"w:color": "red",
"w:space": 0,
"w:sz": 1,
"w:val": "double",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
],
});
});
});
describe("inside horizontal border", () => {
it("should add a table cell inside horizontal border", () => {
const tableBorders = new TableBorders({
insideHorizontal: {
style: BorderStyle.DOUBLE,
size: 1,
color: "red",
},
});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:left": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:right": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "red",
"w:space": 0,
"w:sz": 1,
"w:val": "double",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
],
});
});
});
describe("inside vertical border", () => {
it("should add a table cell inside horizontal border", () => {
const tableBorders = new TableBorders({
insideVertical: {
style: BorderStyle.DOUBLE,
size: 1,
color: "red",
},
});
const tree = new Formatter().format(tableBorders);
expect(tree).to.deep.equal({
"w:tblBorders": [
{
"w:top": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:left": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:right": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideH": {
_attr: {
"w:color": "auto",
"w:space": 0,
"w:sz": 4,
"w:val": "single",
},
},
},
{
"w:insideV": {
_attr: {
"w:color": "red",
"w:space": 0,
"w:sz": 1,
"w:val": "double",
},
},
},
],
});
});
});
});
});

View File

@ -1,14 +1,95 @@
// http://officeopenxml.com/WPtableBorders.php
import { BorderStyle } from "file/styles";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export interface ITableBordersOptions {
readonly top?: {
readonly style: BorderStyle;
readonly size: number;
readonly color: string;
};
readonly bottom?: {
readonly style: BorderStyle;
readonly size: number;
readonly color: string;
};
readonly left?: {
readonly style: BorderStyle;
readonly size: number;
readonly color: string;
};
readonly right?: {
readonly style: BorderStyle;
readonly size: number;
readonly color: string;
};
readonly insideHorizontal?: {
readonly style: BorderStyle;
readonly size: number;
readonly color: string;
};
readonly insideVertical?: {
readonly style: BorderStyle;
readonly size: number;
readonly color: string;
};
}
export class TableBorders extends XmlComponent {
constructor() {
constructor(options: ITableBordersOptions) {
super("w:tblBorders");
this.root.push(new TableBordersElement("w:top", "single", 4, 0, "auto"));
this.root.push(new TableBordersElement("w:left", "single", 4, 0, "auto"));
this.root.push(new TableBordersElement("w:bottom", "single", 4, 0, "auto"));
this.root.push(new TableBordersElement("w:right", "single", 4, 0, "auto"));
this.root.push(new TableBordersElement("w:insideH", "single", 4, 0, "auto"));
this.root.push(new TableBordersElement("w:insideV", "single", 4, 0, "auto"));
if (options.top) {
this.root.push(new TableBordersElement("w:top", options.top.style, options.top.size, 0, options.top.color));
} else {
this.root.push(new TableBordersElement("w:top", BorderStyle.SINGLE, 4, 0, "auto"));
}
if (options.left) {
this.root.push(new TableBordersElement("w:left", options.left.style, options.left.size, 0, options.left.color));
} else {
this.root.push(new TableBordersElement("w:left", BorderStyle.SINGLE, 4, 0, "auto"));
}
if (options.bottom) {
this.root.push(new TableBordersElement("w:bottom", options.bottom.style, options.bottom.size, 0, options.bottom.color));
} else {
this.root.push(new TableBordersElement("w:bottom", BorderStyle.SINGLE, 4, 0, "auto"));
}
if (options.right) {
this.root.push(new TableBordersElement("w:right", options.right.style, options.right.size, 0, options.right.color));
} else {
this.root.push(new TableBordersElement("w:right", BorderStyle.SINGLE, 4, 0, "auto"));
}
if (options.insideHorizontal) {
this.root.push(
new TableBordersElement(
"w:insideH",
options.insideHorizontal.style,
options.insideHorizontal.size,
0,
options.insideHorizontal.color,
),
);
} else {
this.root.push(new TableBordersElement("w:insideH", BorderStyle.SINGLE, 4, 0, "auto"));
}
if (options.insideVertical) {
this.root.push(
new TableBordersElement(
"w:insideV",
options.insideVertical.style,
options.insideVertical.size,
0,
options.insideVertical.color,
),
);
} else {
this.root.push(new TableBordersElement("w:insideV", BorderStyle.SINGLE, 4, 0, "auto"));
}
}
}

View File

@ -2,7 +2,7 @@ import { IgnoreIfEmptyXmlComponent } from "file/xml-components";
import { ITableShadingAttributesProperties, TableShading } from "../shading";
import { WidthType } from "../table-cell";
import { TableBorders } from "./table-borders";
import { ITableBordersOptions, TableBorders } from "./table-borders";
import { TableCellMargin } from "./table-cell-margin";
import { ITableFloatOptions, TableFloatProperties } from "./table-float-properties";
import { TableLayout, TableLayoutType } from "./table-layout";
@ -27,8 +27,8 @@ export class TableProperties extends IgnoreIfEmptyXmlComponent {
this.root.push(new TableLayout(type));
}
public setBorder(): TableProperties {
this.root.push(new TableBorders());
public setBorder(borderOptions: ITableBordersOptions): TableProperties {
this.root.push(new TableBorders(borderOptions));
return this;
}

View File

@ -2,7 +2,7 @@
import { XmlComponent } from "file/xml-components";
import { TableGrid } from "./grid";
import { TableCell, VerticalMergeType, WidthType } from "./table-cell";
import { ITableFloatOptions, TableProperties } from "./table-properties";
import { ITableBordersOptions, ITableFloatOptions, TableProperties } from "./table-properties";
import { TableLayoutType } from "./table-properties/table-layout";
import { TableRow } from "./table-row";
@ -32,6 +32,7 @@ export interface ITableOptions {
};
readonly float?: ITableFloatOptions;
readonly layout?: TableLayoutType;
readonly borders?: ITableBordersOptions;
}
export class Table extends XmlComponent {
@ -44,11 +45,17 @@ export class Table extends XmlComponent {
margins: { marginUnitType, top, bottom, right, left } = { marginUnitType: WidthType.AUTO, top: 0, bottom: 0, right: 0, left: 0 },
float,
layout,
borders,
}: ITableOptions) {
super("w:tbl");
this.properties = new TableProperties();
this.root.push(this.properties);
this.properties.setBorder();
if (borders) {
this.properties.setBorder(borders);
} else {
this.properties.setBorder({});
}
if (width) {
this.properties.setWidth(width.size, width.type);