diff --git a/demo/2-declaritive-styles.ts b/demo/2-declaritive-styles.ts index 34d7e19e70..472b916091 100644 --- a/demo/2-declaritive-styles.ts +++ b/demo/2-declaritive-styles.ts @@ -1,7 +1,7 @@ // Example on how to customise the look at feel using Styles // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build"; +import { AlignmentType, Document, HeadingLevel, Packer, Paragraph, TextRun, UnderlineType } from "../build"; const doc = new Document({ creator: "Clippy", @@ -83,15 +83,23 @@ const doc = new Document({ }, ], }, + numbering: { + config: [ + { + reference: "my-crazy-numbering", + levels: [ + { + level: 0, + format: "lowerLetter", + text: "%1)", + alignment: AlignmentType.LEFT, + }, + ], + }, + ], + }, }); -const numberedAbstract = doc.Numbering.createAbstractNumbering(); -numberedAbstract.createLevel(0, "lowerLetter", "%1)", "left"); - -const letterNumbering = doc.Numbering.createConcreteNumbering(numberedAbstract); -const letterNumbering5 = doc.Numbering.createConcreteNumbering(numberedAbstract); -letterNumbering5.overrideLevel(0, 5); - doc.addSection({ children: [ new Paragraph({ @@ -106,21 +114,21 @@ doc.addSection({ new Paragraph({ text: "Option1", numbering: { - num: letterNumbering, + reference: "my-crazy-numbering", level: 0, }, }), new Paragraph({ text: "Option5 -- override 2 to 5", numbering: { - num: letterNumbering, + reference: "my-crazy-numbering", level: 0, }, }), new Paragraph({ text: "Option3", numbering: { - num: letterNumbering, + reference: "my-crazy-numbering", level: 0, }, }), diff --git a/demo/29-numbered-lists.ts b/demo/29-numbered-lists.ts index 740320e5f8..18da5cc359 100644 --- a/demo/29-numbered-lists.ts +++ b/demo/29-numbered-lists.ts @@ -1,23 +1,37 @@ // Numbered lists // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Numbering, Packer, Paragraph } from "../build"; +import { AlignmentType, Document, Packer, Paragraph } from "../build"; -const doc = new Document(); - -const numbering = new Numbering(); - -const abstractNum = numbering.createAbstractNumbering(); -abstractNum.createLevel(0, "upperRoman", "%1", "start").indent({ left: 720, hanging: 260 }); - -const concrete = numbering.createConcreteNumbering(abstractNum); +const doc = new Document({ + numbering: { + config: [ + { + levels: [ + { + level: 0, + format: "upperRoman", + text: "%1", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: 720, hanging: 260 }, + }, + }, + }, + ], + reference: "my-crazy-reference", + }, + ], + }, +}); doc.addSection({ children: [ new Paragraph({ text: "line with contextual spacing", numbering: { - num: concrete, + reference: "my-crazy-reference", level: 0, }, contextualSpacing: true, @@ -28,7 +42,7 @@ doc.addSection({ new Paragraph({ text: "line with contextual spacing", numbering: { - num: concrete, + reference: "my-crazy-reference", level: 0, }, contextualSpacing: true, @@ -39,7 +53,7 @@ doc.addSection({ new Paragraph({ text: "line without contextual spacing", numbering: { - num: concrete, + reference: "my-crazy-reference", level: 0, }, contextualSpacing: false, @@ -50,7 +64,7 @@ doc.addSection({ new Paragraph({ text: "line without contextual spacing", numbering: { - num: concrete, + reference: "my-crazy-reference", level: 0, }, contextualSpacing: false, diff --git a/demo/3-numbering-and-bullet-points.ts b/demo/3-numbering-and-bullet-points.ts index e87e6616d2..72b33d2e8e 100644 --- a/demo/3-numbering-and-bullet-points.ts +++ b/demo/3-numbering-and-bullet-points.ts @@ -1,46 +1,80 @@ // Numbering and bullet points example // Import from 'docx' rather than '../build' if you install from npm import * as fs from "fs"; -import { Document, Numbering, Packer, Paragraph } from "../build"; +import { AlignmentType, Document, Packer, Paragraph } from "../build"; -const doc = new Document(); - -const numbering = new Numbering(); - -const abstractNum = numbering.createAbstractNumbering(); -abstractNum.createLevel(0, "upperRoman", "%1", "start").indent({ left: 720, hanging: 260 }); -abstractNum.createLevel(1, "decimal", "%2.", "start").indent({ left: 1440, hanging: 980 }); -abstractNum.createLevel(2, "lowerLetter", "%3)", "start").indent({ left: 2160, hanging: 1700 }); - -const concrete = numbering.createConcreteNumbering(abstractNum); +const doc = new Document({ + numbering: { + config: [ + { + reference: "my-crazy-numbering", + levels: [ + { + level: 0, + format: "upperRoman", + text: "%1", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: 720, hanging: 260 }, + }, + }, + }, + { + level: 1, + format: "decimal", + text: "%2.", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: 1440, hanging: 980 }, + }, + }, + }, + { + level: 2, + format: "lowerLetter", + text: "%3)", + alignment: AlignmentType.START, + style: { + paragraph: { + indent: { left: 2160, hanging: 1700 }, + }, + }, + }, + ], + }, + ], + }, +}); doc.addSection({ children: [ new Paragraph({ text: "Hey you", numbering: { - num: concrete, + reference: "my-crazy-numbering", level: 0, }, }), new Paragraph({ text: "What's up fam", numbering: { - num: concrete, + reference: "my-crazy-numbering", level: 1, }, }), new Paragraph({ text: "Hello World 2", numbering: { - num: concrete, + reference: "my-crazy-numbering", level: 1, }, }), new Paragraph({ text: "Yeah boi", numbering: { - num: concrete, + reference: "my-crazy-numbering", level: 2, }, }), diff --git a/src/export/packer/next-compiler.ts b/src/export/packer/next-compiler.ts index 6a3e752098..bff24a8ef7 100644 --- a/src/export/packer/next-compiler.ts +++ b/src/export/packer/next-compiler.ts @@ -4,6 +4,7 @@ import * as xml from "xml"; import { File } from "file"; import { Formatter } from "../formatter"; import { ImageReplacer } from "./image-replacer"; +import { NumberingReplacer } from "./numbering-replacer"; interface IXmlifyedFile { readonly data: string; @@ -30,10 +31,12 @@ interface IXmlifyedFileMapping { export class Compiler { private readonly formatter: Formatter; private readonly imageReplacer: ImageReplacer; + private readonly numberingReplacer: NumberingReplacer; constructor() { this.formatter = new Formatter(); this.imageReplacer = new ImageReplacer(); + this.numberingReplacer = new NumberingReplacer(); } public compile(file: File, prettifyXml?: boolean): JSZip { @@ -89,8 +92,9 @@ export class Compiler { Document: { data: (() => { const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount); + const referenedXmlData = this.numberingReplacer.replace(xmlData, file.Numbering.ConcreteNumbering); - return xmlData; + return referenedXmlData; })(), path: "word/document.xml", }, diff --git a/src/export/packer/numbering-replacer.ts b/src/export/packer/numbering-replacer.ts index e69de29bb2..4ac5dcbf1d 100644 --- a/src/export/packer/numbering-replacer.ts +++ b/src/export/packer/numbering-replacer.ts @@ -0,0 +1,17 @@ +import { ConcreteNumbering } from "file"; + +export class NumberingReplacer { + public replace(xmlData: string, concreteNumberings: ConcreteNumbering[]): string { + let currentXmlData = xmlData; + + for (const concreteNumbering of concreteNumberings) { + if (!concreteNumbering.reference) { + continue; + } + + currentXmlData = currentXmlData.replace(new RegExp(`{${concreteNumbering.reference}}`, "g"), concreteNumbering.id.toString()); + } + + return currentXmlData; + } +} diff --git a/src/file/core-properties/properties.ts b/src/file/core-properties/properties.ts index e86a0732d0..8ac2130310 100644 --- a/src/file/core-properties/properties.ts +++ b/src/file/core-properties/properties.ts @@ -1,7 +1,7 @@ import { XmlComponent } from "file/xml-components"; import { DocumentAttributes } from "../document/document-attributes"; -import { IAbstractNumberingOptions } from "../numbering"; +import { INumberingOptions } from "../numbering"; import { IStylesOptions } from "../styles"; import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components"; @@ -15,7 +15,7 @@ export interface IPropertiesOptions { readonly revision?: string; readonly externalStyles?: string; readonly styles?: IStylesOptions; - readonly numbering?: IAbstractNumberingOptions; + readonly numbering?: INumberingOptions; } export class CoreProperties extends XmlComponent { diff --git a/src/file/file.ts b/src/file/file.ts index 63bb272596..6d46fdb338 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -71,9 +71,13 @@ export class File { sections: ISectionOptions[] = [], ) { this.coreProperties = new CoreProperties(options); - this.numbering = new Numbering({ - levels: options.numbering ? options.numbering.levels : [], - }); + this.numbering = new Numbering( + options.numbering + ? options.numbering + : { + config: [], + }, + ); this.docRelationships = new Relationships(); this.fileRelationships = new Relationships(); this.appProperties = new AppProperties(); diff --git a/src/file/numbering/abstract-numbering.spec.ts b/src/file/numbering/abstract-numbering.spec.ts index 6552775cd5..59655aa40e 100644 --- a/src/file/numbering/abstract-numbering.spec.ts +++ b/src/file/numbering/abstract-numbering.spec.ts @@ -10,24 +10,20 @@ import { AbstractNumbering } from "./abstract-numbering"; describe("AbstractNumbering", () => { it("stores its ID at its .id property", () => { - const abstractNumbering = new AbstractNumbering(5, { - levels: [], - }); + const abstractNumbering = new AbstractNumbering(5, []); expect(abstractNumbering.id).to.equal(5); }); describe("#createLevel", () => { it("creates a level with the given characteristics", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 3, - format: "lowerLetter", - text: "%1)", - alignment: AlignmentType.END, - }, - ], - }); + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 3, + format: "lowerLetter", + text: "%1)", + alignment: AlignmentType.END, + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } }); @@ -37,15 +33,13 @@ describe("AbstractNumbering", () => { }); it("uses 'start' as the default alignment", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 3, - format: "lowerLetter", - text: "%1)", - }, - ], - }); + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 3, + format: "lowerLetter", + text: "%1)", + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } }); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } }); @@ -56,20 +50,18 @@ describe("AbstractNumbering", () => { describe("formatting methods: paragraph properties", () => { it("#indent", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - indent: { left: 720 }, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + indent: { left: 720 }, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }], @@ -77,20 +69,18 @@ describe("AbstractNumbering", () => { }); it("#spacing", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - spacing: { before: 50, after: 150 }, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + spacing: { before: 50, after: 150 }, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }], @@ -98,20 +88,18 @@ describe("AbstractNumbering", () => { }); it("#center", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - alignment: AlignmentType.CENTER, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + alignment: AlignmentType.CENTER, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }], @@ -119,20 +107,18 @@ describe("AbstractNumbering", () => { }); it("#left", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - alignment: AlignmentType.LEFT, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + alignment: AlignmentType.LEFT, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }], @@ -140,20 +126,18 @@ describe("AbstractNumbering", () => { }); it("#right", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - alignment: AlignmentType.RIGHT, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + alignment: AlignmentType.RIGHT, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }], @@ -161,20 +145,18 @@ describe("AbstractNumbering", () => { }); it("#justified", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - alignment: AlignmentType.JUSTIFIED, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + alignment: AlignmentType.JUSTIFIED, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }], @@ -182,20 +164,18 @@ describe("AbstractNumbering", () => { }); it("#thematicBreak", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - thematicBreak: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + thematicBreak: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [ @@ -218,20 +198,18 @@ describe("AbstractNumbering", () => { }); it("#leftTabStop", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - leftTabStop: 1200, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + leftTabStop: 1200, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [ @@ -243,20 +221,18 @@ describe("AbstractNumbering", () => { }); it("#maxRightTabStop", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - rightTabStop: TabStopPosition.MAX, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + rightTabStop: TabStopPosition.MAX, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [ @@ -268,20 +244,18 @@ describe("AbstractNumbering", () => { }); it("#keepLines", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - keepLines: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + keepLines: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:keepLines": EMPTY_OBJECT }], @@ -289,20 +263,18 @@ describe("AbstractNumbering", () => { }); it("#keepNext", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - paragraph: { - keepNext: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + paragraph: { + keepNext: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:pPr": [{ "w:keepNext": EMPTY_OBJECT }], @@ -312,20 +284,18 @@ describe("AbstractNumbering", () => { describe("formatting methods: run properties", () => { it("#size", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - size: 24, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + size: 24, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }], @@ -333,20 +303,18 @@ describe("AbstractNumbering", () => { }); it("#smallCaps", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - smallCaps: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + smallCaps: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }], @@ -354,20 +322,18 @@ describe("AbstractNumbering", () => { }); it("#allCaps", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - allCaps: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + allCaps: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }], @@ -375,20 +341,18 @@ describe("AbstractNumbering", () => { }); it("#strike", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - strike: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + strike: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ @@ -397,20 +361,18 @@ describe("AbstractNumbering", () => { }); it("#doubleStrike", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - doubleStrike: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + doubleStrike: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }], @@ -418,20 +380,18 @@ describe("AbstractNumbering", () => { }); it("#subScript", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - subScript: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + subScript: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }], @@ -439,20 +399,18 @@ describe("AbstractNumbering", () => { }); it("#superScript", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - superScript: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + superScript: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }], @@ -460,20 +418,18 @@ describe("AbstractNumbering", () => { }); it("#font", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - font: "Times", - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + font: "Times", }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [ @@ -483,20 +439,18 @@ describe("AbstractNumbering", () => { }); it("#bold", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - bold: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + bold: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:b": { _attr: { "w:val": true } } }], @@ -504,20 +458,18 @@ describe("AbstractNumbering", () => { }); it("#italics", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - italics: true, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + italics: true, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:i": { _attr: { "w:val": true } } }], @@ -525,20 +477,18 @@ describe("AbstractNumbering", () => { }); it("#highlight", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - highlight: "005599", - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + highlight: "005599", }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:highlight": { _attr: { "w:val": "005599" } } }], @@ -546,24 +496,22 @@ describe("AbstractNumbering", () => { }); it("#shadow", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - shadow: { - type: ShadingType.PERCENT_10, - fill: "00FFFF", - color: "FF0000", - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + shadow: { + type: ShadingType.PERCENT_10, + fill: "00FFFF", + color: "FF0000", }, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:shd": { _attr: { "w:val": "pct10", "w:fill": "00FFFF", "w:color": "FF0000" } } }], @@ -572,20 +520,18 @@ describe("AbstractNumbering", () => { describe("#underline", () => { it("should set underline to 'single' if no arguments are given", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - underline: {}, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + underline: {}, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }], @@ -593,22 +539,20 @@ describe("AbstractNumbering", () => { }); it("should set the style if given", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - underline: { - type: UnderlineType.DOUBLE, - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + underline: { + type: UnderlineType.DOUBLE, }, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }], @@ -616,23 +560,21 @@ describe("AbstractNumbering", () => { }); it("should set the style and color if given", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - underline: { - type: UnderlineType.DOUBLE, - color: "005599", - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + underline: { + type: UnderlineType.DOUBLE, + color: "005599", }, }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }], @@ -641,20 +583,18 @@ describe("AbstractNumbering", () => { }); it("#color", () => { - const abstractNumbering = new AbstractNumbering(1, { - levels: [ - { - level: 0, - format: "lowerRoman", - text: "%0.", - style: { - run: { - color: "123456", - }, + const abstractNumbering = new AbstractNumbering(1, [ + { + level: 0, + format: "lowerRoman", + text: "%0.", + style: { + run: { + color: "123456", }, }, - ], - }); + }, + ]); const tree = new Formatter().format(abstractNumbering); expect(tree["w:abstractNum"][2]["w:lvl"]).to.include({ "w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }], diff --git a/src/file/numbering/abstract-numbering.ts b/src/file/numbering/abstract-numbering.ts index 49b1175812..2eed73fe39 100644 --- a/src/file/numbering/abstract-numbering.ts +++ b/src/file/numbering/abstract-numbering.ts @@ -15,14 +15,10 @@ class AbstractNumberingAttributes extends XmlAttributeComponent { export class ConcreteNumbering extends XmlComponent { public readonly id: number; - constructor(numId: number, abstractNumId: number) { + constructor(numId: number, abstractNumId: number, public readonly reference?: string) { super("w:num"); this.root.push( new NumAttributes({ diff --git a/src/file/numbering/numbering.spec.ts b/src/file/numbering/numbering.spec.ts index 0c16884300..79a824b67a 100644 --- a/src/file/numbering/numbering.spec.ts +++ b/src/file/numbering/numbering.spec.ts @@ -8,7 +8,7 @@ describe("Numbering", () => { describe("#constructor", () => { it("creates a default numbering with one abstract and one concrete instance", () => { const numbering = new Numbering({ - levels: [], + config: [], }); const tree = new Formatter().format(numbering); diff --git a/src/file/numbering/numbering.ts b/src/file/numbering/numbering.ts index 891aa2ab3a..b8747991d0 100644 --- a/src/file/numbering/numbering.ts +++ b/src/file/numbering/numbering.ts @@ -3,17 +3,25 @@ import { AlignmentType } from "file/paragraph"; import { IXmlableObject, XmlComponent } from "file/xml-components"; import { DocumentAttributes } from "../document/document-attributes"; -import { AbstractNumbering, IAbstractNumberingOptions } from "./abstract-numbering"; +import { AbstractNumbering } from "./abstract-numbering"; +import { ILevelsOptions } from "./level"; import { ConcreteNumbering } from "./num"; +export interface INumberingOptions { + readonly config: Array<{ + readonly levels: ILevelsOptions[]; + readonly reference: string; + }>; +} + export class Numbering extends XmlComponent { // tslint:disable-next-line:readonly-keyword private nextId: number; - private readonly abstractNumbering: XmlComponent[] = []; - private readonly concreteNumbering: XmlComponent[] = []; + private readonly abstractNumbering: AbstractNumbering[] = []; + private readonly concreteNumbering: ConcreteNumbering[] = []; - constructor(options: IAbstractNumberingOptions) { + constructor(options: INumberingOptions) { super("w:numbering"); this.root.push( new DocumentAttributes({ @@ -39,114 +47,114 @@ export class Numbering extends XmlComponent { this.nextId = 0; - const abstractNumbering = this.createAbstractNumbering({ - levels: [ - { - level: 0, - format: "bullet", - text: "\u25CF", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 720, hanging: 360 }, - }, + const abstractNumbering = this.createAbstractNumbering([ + { + level: 0, + format: "bullet", + text: "\u25CF", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 720, hanging: 360 }, }, }, - { - level: 1, - format: "bullet", - text: "\u25CB", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 1440, hanging: 360 }, - }, + }, + { + level: 1, + format: "bullet", + text: "\u25CB", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 1440, hanging: 360 }, }, }, - { - level: 2, - format: "bullet", - text: "\u25A0", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 2160, hanging: 360 }, - }, + }, + { + level: 2, + format: "bullet", + text: "\u25A0", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 2160, hanging: 360 }, }, }, - { - level: 3, - format: "bullet", - text: "\u25CF", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 2880, hanging: 360 }, - }, + }, + { + level: 3, + format: "bullet", + text: "\u25CF", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 2880, hanging: 360 }, }, }, - { - level: 4, - format: "bullet", - text: "\u25CB", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 3600, hanging: 360 }, - }, + }, + { + level: 4, + format: "bullet", + text: "\u25CB", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 3600, hanging: 360 }, }, }, - { - level: 5, - format: "bullet", - text: "\u25A0", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 4320, hanging: 360 }, - }, + }, + { + level: 5, + format: "bullet", + text: "\u25A0", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 4320, hanging: 360 }, }, }, - { - level: 6, - format: "bullet", - text: "\u25CF", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 5040, hanging: 360 }, - }, + }, + { + level: 6, + format: "bullet", + text: "\u25CF", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 5040, hanging: 360 }, }, }, - { - level: 7, - format: "bullet", - text: "\u25CF", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 5760, hanging: 360 }, - }, + }, + { + level: 7, + format: "bullet", + text: "\u25CF", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 5760, hanging: 360 }, }, }, - { - level: 8, - format: "bullet", - text: "\u25CF", - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { left: 6480, hanging: 360 }, - }, + }, + { + level: 8, + format: "bullet", + text: "\u25CF", + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { left: 6480, hanging: 360 }, }, }, - ], - }); + }, + ]); this.createConcreteNumbering(abstractNumbering); - const currentAbstractNumbering = this.createAbstractNumbering(options); - this.createConcreteNumbering(currentAbstractNumbering); + for (const con of options.config) { + const currentAbstractNumbering = this.createAbstractNumbering(con.levels); + this.createConcreteNumbering(currentAbstractNumbering, con.reference); + } } public prepForXml(): IXmlableObject | undefined { @@ -155,15 +163,19 @@ export class Numbering extends XmlComponent { return super.prepForXml(); } - private createConcreteNumbering(abstractNumbering: AbstractNumbering): ConcreteNumbering { - const num = new ConcreteNumbering(this.nextId++, abstractNumbering.id); + private createConcreteNumbering(abstractNumbering: AbstractNumbering, reference?: string): ConcreteNumbering { + const num = new ConcreteNumbering(this.nextId++, abstractNumbering.id, reference); this.concreteNumbering.push(num); return num; } - private createAbstractNumbering(options: IAbstractNumberingOptions): AbstractNumbering { + private createAbstractNumbering(options: ILevelsOptions[]): AbstractNumbering { const num = new AbstractNumbering(this.nextId++, options); this.abstractNumbering.push(num); return num; } + + public get ConcreteNumbering(): ConcreteNumbering[] { + return this.concreteNumbering; + } } diff --git a/src/file/paragraph/formatting/unordered-list.ts b/src/file/paragraph/formatting/unordered-list.ts index f7650fc844..bd1bcb562a 100644 --- a/src/file/paragraph/formatting/unordered-list.ts +++ b/src/file/paragraph/formatting/unordered-list.ts @@ -24,7 +24,7 @@ class NumberId extends XmlComponent { super("w:numId"); this.root.push( new Attributes({ - val: typeof id === "string" ? `__${id}__` : id, + val: typeof id === "string" ? `{${id}}` : id, }), ); } diff --git a/src/file/paragraph/paragraph.spec.ts b/src/file/paragraph/paragraph.spec.ts index 99c8efb0c0..b4a797a8c2 100644 --- a/src/file/paragraph/paragraph.spec.ts +++ b/src/file/paragraph/paragraph.spec.ts @@ -629,7 +629,7 @@ describe("Paragraph", () => { "w:pPr": [ { "w:pStyle": { _attr: { "w:val": "ListParagraph" } } }, { - "w:numPr": [{ "w:ilvl": { _attr: { "w:val": 0 } } }, { "w:numId": { _attr: { "w:val": "__test id__" } } }], + "w:numPr": [{ "w:ilvl": { _attr: { "w:val": 0 } } }, { "w:numId": { _attr: { "w:val": "{test id}" } } }], }, ], },