Merge pull request #484 from dolanmiu/feat/right-indent

Multiple paragraphs in footnotes
This commit is contained in:
Dolan
2021-03-08 04:43:08 +00:00
committed by GitHub
15 changed files with 250 additions and 70 deletions

8
.nycrc
View File

@ -1,9 +1,9 @@
{ {
"check-coverage": true, "check-coverage": true,
"lines": 97.77, "lines": 97.86,
"functions": 93.89, "functions": 94.33,
"branches": 94.83, "branches": 94.99,
"statements": 97.75, "statements": 97.85,
"include": [ "include": [
"src/**/*.ts" "src/**/*.ts"
], ],

View File

@ -4,14 +4,14 @@ import * as fs from "fs";
import { Document, FootnoteReferenceRun, Packer, Paragraph, TextRun } from "../build"; import { Document, FootnoteReferenceRun, Packer, Paragraph, TextRun } from "../build";
const doc = new Document({ const doc = new Document({
footnotes: [ footnotes: {
new Paragraph("Foo"), 1: { children: [new Paragraph("Foo"), new Paragraph("Bar")] },
new Paragraph("Test"), 2: { children: [new Paragraph("Test")] },
new Paragraph("My amazing reference"), 3: { children: [new Paragraph("My amazing reference")] },
new Paragraph("Foo1"), 4: { children: [new Paragraph("Foo1")] },
new Paragraph("Test1"), 5: { children: [new Paragraph("Test1")] },
new Paragraph("My amazing reference1"), 6: { children: [new Paragraph("My amazing reference1")] },
], },
}); });
doc.addSection({ doc.addSection({

View File

@ -33,7 +33,12 @@ doc.addSection({
children: [ children: [
new Paragraph({ new Paragraph({
heading: HeadingLevel.HEADING_1, heading: HeadingLevel.HEADING_1,
children: [new Bookmark("myAnchorId", "Lorem Ipsum")], children: [
new Bookmark({
id: "myAnchorId",
children: [new TextRun("Lorem Ipsum")],
}),
],
}), }),
new Paragraph("\n"), new Paragraph("\n"),
new Paragraph(LOREM_IPSUM), new Paragraph(LOREM_IPSUM),

View File

@ -19,7 +19,11 @@ export interface IPropertiesOptions {
readonly externalStyles?: string; readonly externalStyles?: string;
readonly styles?: IStylesOptions; readonly styles?: IStylesOptions;
readonly numbering?: INumberingOptions; readonly numbering?: INumberingOptions;
readonly footnotes?: Paragraph[]; readonly footnotes?: {
readonly [key: string]: {
readonly children: Paragraph[];
};
};
readonly background?: IDocumentBackgroundOptions; readonly background?: IDocumentBackgroundOptions;
readonly features?: { readonly features?: {
readonly trackRevisions?: boolean; readonly trackRevisions?: boolean;

View File

@ -240,7 +240,11 @@ describe("File", () => {
describe("#createFootnote", () => { describe("#createFootnote", () => {
it("should create footnote", () => { it("should create footnote", () => {
const wrapper = new File({ const wrapper = new File({
footnotes: [new Paragraph("hello")], footnotes: {
1: {
children: [new Paragraph("hello")],
},
},
}); });
const tree = new Formatter().format(wrapper.FootNotes.View); const tree = new Formatter().format(wrapper.FootNotes.View);

View File

@ -139,8 +139,9 @@ export class File {
} }
if (options.footnotes) { if (options.footnotes) {
for (const paragraph of options.footnotes) { // tslint:disable-next-line: forin
this.footnotesWrapper.View.createFootNote(paragraph); for (const key in options.footnotes) {
this.footnotesWrapper.View.createFootNote(parseFloat(key), options.footnotes[key].children);
} }
} }

View File

@ -1,11 +1,9 @@
import { XmlAttributeComponent } from "file/xml-components"; import { XmlAttributeComponent } from "file/xml-components";
export interface IFootnoteAttributesProperties { export class FootnoteAttributes extends XmlAttributeComponent<{
readonly type?: string; readonly type?: string;
readonly id: number; readonly id: number;
} }> {
export class FootnoteAttributes extends XmlAttributeComponent<IFootnoteAttributesProperties> {
protected readonly xmlKeys = { protected readonly xmlKeys = {
type: "w:type", type: "w:type",
id: "w:id", id: "w:id",

View File

@ -1,6 +1,7 @@
import { expect } from "chai"; import { expect } from "chai";
import { Formatter } from "export/formatter"; import { Formatter } from "export/formatter";
import { Paragraph, TextRun } from "file/paragraph";
import { Footnote, FootnoteType } from "./footnote"; import { Footnote, FootnoteType } from "./footnote";
@ -10,6 +11,7 @@ describe("Footnote", () => {
const footnote = new Footnote({ const footnote = new Footnote({
id: 1, id: 1,
type: FootnoteType.SEPERATOR, type: FootnoteType.SEPERATOR,
children: [],
}); });
const tree = new Formatter().format(footnote); const tree = new Formatter().format(footnote);
@ -20,11 +22,141 @@ describe("Footnote", () => {
it("should create a footnote without a footnote type", () => { it("should create a footnote without a footnote type", () => {
const footnote = new Footnote({ const footnote = new Footnote({
id: 1, id: 1,
children: [],
}); });
const tree = new Formatter().format(footnote); const tree = new Formatter().format(footnote);
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]); expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
expect(tree["w:footnote"]).to.deep.equal({ _attr: { "w:id": 1 } }); expect(tree["w:footnote"]).to.deep.equal({ _attr: { "w:id": 1 } });
}); });
it("should append footnote ref run on the first footnote paragraph", () => {
const footnote = new Footnote({
id: 1,
children: [new Paragraph({ children: [new TextRun("test-footnote")] })],
});
const tree = new Formatter().format(footnote);
expect(tree).to.deep.equal({
"w:footnote": [
{
_attr: {
"w:id": 1,
},
},
{
"w:p": [
{
"w:r": [
{
"w:rPr": [
{
"w:rStyle": {
_attr: {
"w:val": "FootnoteReference",
},
},
},
],
},
{
"w:footnoteRef": {},
},
],
},
{
"w:r": [
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test-footnote",
],
},
],
},
],
},
],
});
});
it("should add multiple paragraphs", () => {
const footnote = new Footnote({
id: 1,
children: [
new Paragraph({ children: [new TextRun("test-footnote")] }),
new Paragraph({ children: [new TextRun("test-footnote-2")] }),
],
});
const tree = new Formatter().format(footnote);
expect(tree).to.deep.equal({
"w:footnote": [
{
_attr: {
"w:id": 1,
},
},
{
"w:p": [
{
"w:r": [
{
"w:rPr": [
{
"w:rStyle": {
_attr: {
"w:val": "FootnoteReference",
},
},
},
],
},
{
"w:footnoteRef": {},
},
],
},
{
"w:r": [
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test-footnote",
],
},
],
},
],
},
{
"w:p": [
{
"w:r": [
{
"w:t": [
{
_attr: {
"xml:space": "preserve",
},
},
"test-footnote-2",
],
},
],
},
],
},
],
});
});
}); });
}); });

View File

@ -12,6 +12,7 @@ export enum FootnoteType {
export interface IFootnoteOptions { export interface IFootnoteOptions {
readonly id: number; readonly id: number;
readonly type?: FootnoteType; readonly type?: FootnoteType;
readonly children: Paragraph[];
} }
export class Footnote extends XmlComponent { export class Footnote extends XmlComponent {
@ -23,10 +24,15 @@ export class Footnote extends XmlComponent {
id: options.id, id: options.id,
}), }),
); );
for (let i = 0; i < options.children.length; i++) {
const child = options.children[i];
if (i === 0) {
child.addRunToFront(new FootnoteRefRun());
} }
public add(paragraph: Paragraph): void { this.root.push(child);
paragraph.addRunToFront(new FootnoteRefRun()); }
this.root.push(paragraph);
} }
} }

View File

@ -2,11 +2,9 @@ import { Run } from "file/paragraph/run";
import { Style } from "file/paragraph/run/style"; import { Style } from "file/paragraph/run/style";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export interface IFootNoteReferenceRunAttributesProperties { export class FootNoteReferenceRunAttributes extends XmlAttributeComponent<{
readonly id: number; readonly id: number;
} }> {
export class FootNoteReferenceRunAttributes extends XmlAttributeComponent<IFootNoteReferenceRunAttributesProperties> {
protected readonly xmlKeys = { protected readonly xmlKeys = {
id: "w:id", id: "w:id",
}; };

View File

@ -1,4 +1,5 @@
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { Paragraph } from "../paragraph"; import { Paragraph } from "../paragraph";
import { Footnote, FootnoteType } from "./footnote/footnote"; import { Footnote, FootnoteType } from "./footnote/footnote";
import { ContinuationSeperatorRun } from "./footnote/run/continuation-seperator-run"; import { ContinuationSeperatorRun } from "./footnote/run/continuation-seperator-run";
@ -6,14 +7,9 @@ import { SeperatorRun } from "./footnote/run/seperator-run";
import { FootnotesAttributes } from "./footnotes-attributes"; import { FootnotesAttributes } from "./footnotes-attributes";
export class FootNotes extends XmlComponent { export class FootNotes extends XmlComponent {
// tslint:disable-next-line:readonly-keyword
private currentId: number;
constructor() { constructor() {
super("w:footnotes"); super("w:footnotes");
this.currentId = 1;
this.root.push( this.root.push(
new FootnotesAttributes({ new FootnotesAttributes({
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas", wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
@ -39,8 +35,7 @@ export class FootNotes extends XmlComponent {
const begin = new Footnote({ const begin = new Footnote({
id: -1, id: -1,
type: FootnoteType.SEPERATOR, type: FootnoteType.SEPERATOR,
}); children: [
begin.add(
new Paragraph({ new Paragraph({
spacing: { spacing: {
after: 0, after: 0,
@ -49,14 +44,15 @@ export class FootNotes extends XmlComponent {
}, },
children: [new SeperatorRun()], children: [new SeperatorRun()],
}), }),
); ],
});
this.root.push(begin); this.root.push(begin);
const spacing = new Footnote({ const spacing = new Footnote({
id: 0, id: 0,
type: FootnoteType.CONTINUATION_SEPERATOR, type: FootnoteType.CONTINUATION_SEPERATOR,
}); children: [
spacing.add(
new Paragraph({ new Paragraph({
spacing: { spacing: {
after: 0, after: 0,
@ -65,15 +61,18 @@ export class FootNotes extends XmlComponent {
}, },
children: [new ContinuationSeperatorRun()], children: [new ContinuationSeperatorRun()],
}), }),
); ],
});
this.root.push(spacing); this.root.push(spacing);
} }
public createFootNote(paragraph: Paragraph): void { public createFootNote(id: number, paragraph: Paragraph[]): void {
const footnote = new Footnote({ id: this.currentId }); const footnote = new Footnote({
footnote.add(paragraph); id: id,
this.root.push(footnote); children: paragraph,
});
this.currentId++; this.root.push(footnote);
} }
} }

View File

@ -2,20 +2,24 @@ import { assert, expect } from "chai";
import { Utility } from "tests/utility"; import { Utility } from "tests/utility";
import { TextRun } from "../run";
import { Bookmark } from "./bookmark"; import { Bookmark } from "./bookmark";
describe("Bookmark", () => { describe("Bookmark", () => {
let bookmark: Bookmark; let bookmark: Bookmark;
beforeEach(() => { beforeEach(() => {
bookmark = new Bookmark("anchor", "Internal Link"); bookmark = new Bookmark({
id: "anchor",
children: [new TextRun("Internal Link")],
});
}); });
it("should create a bookmark with three root elements", () => { it("should create a bookmark with three root elements", () => {
const newJson = Utility.jsonify(bookmark); const newJson = Utility.jsonify(bookmark);
assert.equal(newJson.rootKey, undefined); assert.equal(newJson.rootKey, undefined);
assert.equal(newJson.start.rootKey, "w:bookmarkStart"); assert.equal(newJson.start.rootKey, "w:bookmarkStart");
assert.equal(newJson.text.rootKey, "w:r"); assert.equal(newJson.children[0].rootKey, "w:r");
assert.equal(newJson.end.rootKey, "w:bookmarkEnd"); assert.equal(newJson.end.rootKey, "w:bookmarkEnd");
}); });
@ -27,7 +31,7 @@ describe("Bookmark", () => {
it("should create a bookmark with the correct attributes on the text element", () => { it("should create a bookmark with the correct attributes on the text element", () => {
const newJson = Utility.jsonify(bookmark); const newJson = Utility.jsonify(bookmark);
assert.equal(JSON.stringify(newJson.text.root[1].root[1]), JSON.stringify("Internal Link")); assert.equal(JSON.stringify(newJson.children[0].root[1].root[1]), JSON.stringify("Internal Link"));
}); });
it("should create a bookmark with the correct attributes on the bookmark end element", () => { it("should create a bookmark with the correct attributes on the bookmark end element", () => {

View File

@ -6,24 +6,24 @@ import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attri
export class Bookmark { export class Bookmark {
public readonly start: BookmarkStart; public readonly start: BookmarkStart;
public readonly text: TextRun; public readonly children: TextRun[];
public readonly end: BookmarkEnd; public readonly end: BookmarkEnd;
constructor(name: string, text: string) { constructor(options: { readonly id: string; readonly children: TextRun[] }) {
const linkId = shortid.generate().toLowerCase(); const linkId = shortid.generate().toLowerCase();
this.start = new BookmarkStart(name, linkId); this.start = new BookmarkStart(options.id, linkId);
this.text = new TextRun(text); this.children = options.children;
this.end = new BookmarkEnd(linkId); this.end = new BookmarkEnd(linkId);
} }
} }
export class BookmarkStart extends XmlComponent { export class BookmarkStart extends XmlComponent {
constructor(name: string, linkId: string) { constructor(id: string, linkId: string) {
super("w:bookmarkStart"); super("w:bookmarkStart");
const attributes = new BookmarkStartAttributes({ const attributes = new BookmarkStartAttributes({
name, name: id,
id: linkId, id: linkId,
}); });
this.root.push(attributes); this.root.push(attributes);

View File

@ -618,6 +618,28 @@ describe("Paragraph", () => {
], ],
}); });
}); });
it("should not add ListParagraph style when custom is true", () => {
const paragraph = new Paragraph({
numbering: {
reference: "test id",
level: 0,
custom: true,
},
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{
"w:numPr": [{ "w:ilvl": { _attr: { "w:val": 0 } } }, { "w:numId": { _attr: { "w:val": "{test id}" } } }],
},
],
},
],
});
});
}); });
it("it should add bookmark", () => { it("it should add bookmark", () => {
@ -625,7 +647,12 @@ describe("Paragraph", () => {
return "test-unique-id"; return "test-unique-id";
}); });
const paragraph = new Paragraph({ const paragraph = new Paragraph({
children: [new Bookmark("test-id", "test")], children: [
new Bookmark({
id: "test-id",
children: [new TextRun("test")],
}),
],
}); });
const tree = new Formatter().format(paragraph); const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({ expect(tree).to.deep.equal({

View File

@ -64,7 +64,9 @@ export class Paragraph extends XmlComponent {
for (const child of options.children) { for (const child of options.children) {
if (child instanceof Bookmark) { if (child instanceof Bookmark) {
this.root.push(child.start); this.root.push(child.start);
this.root.push(child.text); for (const textRun of child.children) {
this.root.push(textRun);
}
this.root.push(child.end); this.root.push(child.end);
continue; continue;
} }