Merge pull request #182 from dolanmiu/feat/table-clean-up

Tidied up table components
This commit is contained in:
Dolan
2018-10-24 14:33:10 +01:00
committed by GitHub
22 changed files with 324 additions and 265 deletions

View File

@ -1,5 +1,15 @@
# Contribution Guidelines
* Include documentation reference(s) at the top of each file:
```js
// http://officeopenxml.com/WPdocument.php
```
* Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
* Follow the `TSLint` rules
## Always think about the user
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
@ -25,17 +35,33 @@ demo updated // Getting better, but capitalize the first letter
Unesesary coment removed // Make sure to use correct spelling
```
## Writing Code
## No leaky components in API interface
* Include documentation reference(s) at the top of each file:
This mainly applies to the API the end user will consume.
```js
// http://officeopenxml.com/WPdocument.php
```
Try to make method parameters accept primatives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
* Follow Prettier standards, and consider using the [Prettier VSCode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) plugin.
This is so that:
* Follow the `TSLint` rules
1. Imports are much cleaner, no need for:
```js
import { ChildComponent } from "./my-feature/sub-component/deeper/.../my-deep.component";
```
2. This is what I consider "leakage". The code is aware of the underlying implementation of the component.
3. It means the end user does not need to import and create the child component to be injected.
**Do not**
`TableFloatProperties` is a class. The outside world would have to construct the object, and inject it in
```js
public float(tableFloatProperties: TableFloatProperties): Table
```
**Do**
`ITableFloatOptions` is an interface for a JSON of primatives.
```js
public float(tableFloatOptions: ITableFloatOptions): Table
```
## Add vs Create
@ -64,7 +90,7 @@ Getters and Setters are done with a capital letter like so:
```js
public get Level() {
...
}
```
@ -130,7 +156,7 @@ enum WeaponType = {
## Spell correctly, full and in American English
I am not sure where these habit in software development comes from, but I do not believe it is beneficial:
I am not sure where these habits in software development come from, but I do not believe it is beneficial:
**Do not:**
```js

View File

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

View File

@ -1,103 +0,0 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { WidthType } from "./table-cell";
import { TableCellMargin } from "./table-cell-margin";
import { TableFloatProperties } from "./table-float-properties";
export class TableProperties extends XmlComponent {
private readonly cellMargin: TableCellMargin;
constructor() {
super("w:tblPr");
this.cellMargin = new TableCellMargin();
this.root.push(this.cellMargin);
}
public setWidth(type: WidthType, w: number | string): TableProperties {
this.root.push(new PreferredTableWidth(type, w));
return this;
}
public setFixedWidthLayout(): TableProperties {
this.root.push(new TableLayout("fixed"));
return this;
}
public setBorder(): TableProperties {
this.root.push(new TableBorders());
return this;
}
public get CellMargin(): TableCellMargin {
return this.cellMargin;
}
public setTableFloatProperties(tableFloatProperties: TableFloatProperties): TableProperties {
this.root.push(tableFloatProperties);
return this;
}
}
interface ITableWidth {
type: WidthType;
w: number | string;
}
class TableWidthAttributes extends XmlAttributeComponent<ITableWidth> {
protected xmlKeys = { type: "w:type", w: "w:w" };
}
class PreferredTableWidth extends XmlComponent {
constructor(type: WidthType, w: number | string) {
super("w:tblW");
this.root.push(new TableWidthAttributes({ type, w }));
}
}
type TableLayoutOptions = "autofit" | "fixed";
class TableLayoutAttributes extends XmlAttributeComponent<{ type: TableLayoutOptions }> {
protected xmlKeys = { type: "w:type" };
}
class TableLayout extends XmlComponent {
constructor(type: TableLayoutOptions) {
super("w:tblLayout");
this.root.push(new TableLayoutAttributes({ type }));
}
}
class TableBorders extends XmlComponent {
constructor() {
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"));
}
}
class TableBordersElement extends XmlComponent {
constructor(elementName: string, value: string, size: number, space: number, color: string) {
super(elementName);
this.root.push(
new TableBordersAttributes({
value,
size,
space,
color,
}),
);
}
}
class TableBordersAttributes extends XmlAttributeComponent<{ value: string; size: number; space: number; color: string }> {
protected xmlKeys = {
value: "w:val",
size: "w:sz",
space: "w:space",
color: "w:color",
};
}

View File

@ -0,0 +1,2 @@
export * from "./table-cell";
export * from "./table-cell-components";

View File

@ -1,5 +1,5 @@
import { BorderStyle } from "file/styles";
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { BorderStyle } from "../styles";
interface ICellBorder {
style: BorderStyle;

View 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;
}
}

View File

@ -1,8 +1,8 @@
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { BorderStyle } from "../styles";
import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell";
import { Formatter } from "../../../export/formatter";
import { BorderStyle } from "../../styles";
import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell-components";
describe("TableCellBorders", () => {
describe("#prepForXml", () => {

View 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;
}
}

View File

@ -0,0 +1,2 @@
export * from "./table-properties";
export * from "./table-float-properties";

View File

@ -0,0 +1,36 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export class TableBorders extends XmlComponent {
constructor() {
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"));
}
}
class TableBordersElement extends XmlComponent {
constructor(elementName: string, value: string, size: number, space: number, color: string) {
super(elementName);
this.root.push(
new TableBordersAttributes({
value,
size,
space,
color,
}),
);
}
}
class TableBordersAttributes extends XmlAttributeComponent<{ value: string; size: number; space: number; color: string }> {
protected xmlKeys = {
value: "w:val",
size: "w:sz",
space: "w:space",
color: "w:color",
};
}

View File

@ -1,5 +1,6 @@
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { WidthType } from "./table-cell";
import { WidthType } from "../table-cell";
class TableCellMarginAttributes extends XmlAttributeComponent<{ type: WidthType; value: number }> {
protected xmlKeys = { value: "w:w", type: "w:sz" };

View File

@ -1,6 +1,6 @@
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { Formatter } from "../../../export/formatter";
import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType, TableFloatProperties } from "./table-float-properties";
describe("Table Float Properties", () => {

View File

@ -0,0 +1,17 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum TableLayoutType {
AUTOFIT = "autofit",
FIXED = "fixed",
}
class TableLayoutAttributes extends XmlAttributeComponent<{ type: TableLayoutType }> {
protected xmlKeys = { type: "w:type" };
}
export class TableLayout extends XmlComponent {
constructor(type: TableLayoutType) {
super("w:tblLayout");
this.root.push(new TableLayoutAttributes({ type }));
}
}

View File

@ -1,8 +1,8 @@
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { TableProperties } from "./properties";
import { WidthType } from "./table-cell";
import { Formatter } from "../../../export/formatter";
import { WidthType } from "../table-cell";
import { TableProperties } from "./table-properties";
describe("TableProperties", () => {
describe("#constructor", () => {

View File

@ -0,0 +1,43 @@
import { XmlComponent } from "file/xml-components";
import { WidthType } from "../table-cell";
import { TableBorders } from "./table-borders";
import { TableCellMargin } from "./table-cell-margin";
import { ITableFloatOptions, TableFloatProperties } from "./table-float-properties";
import { TableLayout, TableLayoutType } from "./table-layout";
import { PreferredTableWidth } from "./table-width";
export class TableProperties extends XmlComponent {
private readonly cellMargin: TableCellMargin;
constructor() {
super("w:tblPr");
this.cellMargin = new TableCellMargin();
this.root.push(this.cellMargin);
}
public setWidth(type: WidthType, w: number | string): TableProperties {
this.root.push(new PreferredTableWidth(type, w));
return this;
}
public setFixedWidthLayout(): TableProperties {
this.root.push(new TableLayout(TableLayoutType.FIXED));
return this;
}
public setBorder(): TableProperties {
this.root.push(new TableBorders());
return this;
}
public get CellMargin(): TableCellMargin {
return this.cellMargin;
}
public setTableFloatProperties(tableFloatOptions: ITableFloatOptions): TableProperties {
this.root.push(new TableFloatProperties(tableFloatOptions));
return this;
}
}

View File

@ -0,0 +1,19 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { WidthType } from "../table-cell";
interface ITableWidth {
type: WidthType;
w: number | string;
}
class TableWidthAttributes extends XmlAttributeComponent<ITableWidth> {
protected xmlKeys = { type: "w:type", w: "w:w" };
}
export class PreferredTableWidth extends XmlComponent {
constructor(type: WidthType, w: number | string) {
super("w:tblW");
this.root.push(new TableWidthAttributes({ type, w }));
}
}

View File

@ -0,0 +1,2 @@
export * from "./table-row";
export * from "./table-row-properties";

View File

@ -0,0 +1,7 @@
import { XmlComponent } from "file/xml-components";
export class TableRowProperties extends XmlComponent {
constructor() {
super("w:trPr");
}
}

View File

@ -0,0 +1,40 @@
import { XmlComponent } from "file/xml-components";
import { TableCell } from "../table-cell";
import { TableRowProperties } from "./table-row-properties";
export class TableRow extends XmlComponent {
private readonly properties: TableRowProperties;
constructor(private readonly cells: TableCell[]) {
super("w:tr");
this.properties = new TableRowProperties();
this.root.push(this.properties);
cells.forEach((c) => this.root.push(c));
}
public getCell(ix: number): TableCell {
const cell = this.cells[ix];
if (!cell) {
throw Error("Index out of bounds when trying to get cell on row");
}
return cell;
}
public addGridSpan(index: number, cellSpan: number): TableCell {
const remainCell = this.cells[index];
remainCell.CellProperties.addGridSpan(cellSpan);
this.cells.splice(index + 1, cellSpan - 1);
this.root.splice(index + 2, cellSpan - 1);
return remainCell;
}
public mergeCells(startIndex: number, endIndex: number): TableCell {
const cellSpan = endIndex - startIndex + 1;
return this.addGridSpan(startIndex, cellSpan);
}
}

View File

@ -5,7 +5,7 @@ import { Formatter } from "../../export/formatter";
import { Paragraph } from "../paragraph";
import { Table } from "./";
import { WidthType } from "./table-cell";
import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-float-properties";
import { RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType } from "./table-properties";
const DEFAULT_TABLE_PROPERTIES = {
"w:tblBorders": [

View File

@ -1,20 +1,10 @@
// http://officeopenxml.com/WPtableGrid.php
import {
GridSpan,
TableCellBorders,
TableCellShading,
TableCellWidth,
VAlign,
VerticalAlign,
VMerge,
VMergeType,
WidthType,
} from "file/table/table-cell";
import { IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph } from "../paragraph";
import { XmlComponent } from "file/xml-components";
import { TableGrid } from "./grid";
import { TableProperties } from "./properties";
import { ITableFloatOptions, TableFloatProperties } from "./table-float-properties";
import { TableCell, WidthType } from "./table-cell";
import { ITableFloatOptions, TableProperties } from "./table-properties";
import { TableRow } from "./table-row";
export class Table extends XmlComponent {
private readonly properties: TableProperties;
@ -85,8 +75,8 @@ export class Table extends XmlComponent {
return this;
}
public float(tableFloatProperties: ITableFloatOptions): Table {
this.properties.setTableFloatProperties(new TableFloatProperties(tableFloatProperties));
public float(tableFloatOptions: ITableFloatOptions): Table {
this.properties.setTableFloatProperties(tableFloatOptions);
return this;
}
@ -94,128 +84,3 @@ export class Table extends XmlComponent {
return this.properties;
}
}
export class TableRow extends XmlComponent {
private readonly properties: TableRowProperties;
constructor(private readonly cells: TableCell[]) {
super("w:tr");
this.properties = new TableRowProperties();
this.root.push(this.properties);
cells.forEach((c) => this.root.push(c));
}
public getCell(ix: number): TableCell {
const cell = this.cells[ix];
if (!cell) {
throw Error("Index out of bounds when trying to get cell on row");
}
return cell;
}
public addGridSpan(index: number, cellSpan: number): TableCell {
const remainCell = this.cells[index];
remainCell.CellProperties.addGridSpan(cellSpan);
this.cells.splice(index + 1, cellSpan - 1);
this.root.splice(index + 2, cellSpan - 1);
return remainCell;
}
public mergeCells(startIndex: number, endIndex: number): TableCell {
const cellSpan = endIndex - startIndex + 1;
return this.addGridSpan(startIndex, cellSpan);
}
}
export class TableRowProperties extends XmlComponent {
constructor() {
super("w:trPr");
}
}
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;
}
}
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;
}
}