#548 #508 Restart numbered lists

This commit is contained in:
Dolan
2021-03-12 03:58:05 +00:00
parent 9864cdea16
commit 0b88cb0ca5
20 changed files with 430 additions and 163 deletions

View File

@ -1,6 +1,7 @@
// http://officeopenxml.com/WPbookmark.php
import { uniqueId } from "convenience-functions";
import { XmlComponent } from "file/xml-components";
import * as shortid from "shortid";
import { TextRun } from "../run";
import { BookmarkEndAttributes, BookmarkStartAttributes } from "./bookmark-attributes";
@ -10,7 +11,7 @@ export class Bookmark {
public readonly end: BookmarkEnd;
constructor(options: { readonly id: string; readonly children: TextRun[] }) {
const linkId = shortid.generate().toLowerCase();
const linkId = uniqueId();
this.start = new BookmarkStart(options.id, linkId);
this.children = options.children;

View File

@ -1,6 +1,5 @@
// http://officeopenxml.com/WPhyperlink.php
import * as shortid from "shortid";
import { uniqueId } from "convenience-functions";
import { XmlComponent } from "file/xml-components";
import { ParagraphChild } from "../paragraph";
@ -33,7 +32,7 @@ export class ConcreteHyperlink extends XmlComponent {
export class InternalHyperlink extends ConcreteHyperlink {
constructor(options: { readonly child: ParagraphChild; readonly anchor: string }) {
super(options.child, shortid.generate().toLowerCase(), options.anchor);
super(options.child, uniqueId(), options.anchor);
}
}

View File

@ -1,7 +1,7 @@
import { assert, expect } from "chai";
import * as shortid from "shortid";
import { stub } from "sinon";
import { SinonStub, stub } from "sinon";
import * as convenienceFunctions from "convenience-functions";
import { Formatter } from "export/formatter";
import { EMPTY_OBJECT } from "file/xml-components";
@ -14,6 +14,16 @@ import { Paragraph } from "./paragraph";
import { TextRun } from "./run";
describe("Paragraph", () => {
before(() => {
stub(convenienceFunctions, "uniqueId").callsFake(() => {
return "test-unique-id";
});
});
after(() => {
(convenienceFunctions.uniqueId as SinonStub).restore();
});
describe("#constructor()", () => {
it("should create valid JSON", () => {
const paragraph = new Paragraph("");
@ -603,6 +613,7 @@ describe("Paragraph", () => {
numbering: {
reference: "test id",
level: 0,
instance: 4,
},
});
const tree = new Formatter().format(paragraph);
@ -612,7 +623,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-4}" } } }],
},
],
},
@ -634,7 +645,7 @@ describe("Paragraph", () => {
{
"w:pPr": [
{
"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-0}" } } }],
},
],
},
@ -644,9 +655,6 @@ describe("Paragraph", () => {
});
it("it should add bookmark", () => {
stub(shortid, "generate").callsFake(() => {
return "test-unique-id";
});
const paragraph = new Paragraph({
children: [
new Bookmark({

View File

@ -1,6 +1,6 @@
// http://officeopenxml.com/WPparagraph.php
import * as shortid from "shortid";
import { uniqueId } from "convenience-functions";
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
import { IContext, IXmlableObject, XmlComponent } from "file/xml-components";
@ -79,7 +79,7 @@ export class Paragraph extends XmlComponent {
for (const element of this.root) {
if (element instanceof ExternalHyperlink) {
const index = this.root.indexOf(element);
const concreteHyperlink = new ConcreteHyperlink(element.options.child, shortid.generate().toLowerCase());
const concreteHyperlink = new ConcreteHyperlink(element.options.child, uniqueId());
context.viewWrapper.Relationships.createRelationship(
concreteHyperlink.linkId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",

View File

@ -0,0 +1,69 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { DocumentWrapper } from "../document-wrapper";
import { File } from "../file";
import { ParagraphProperties } from "./properties";
describe("ParagraphProperties", () => {
describe("#constructor()", () => {
it("creates an initially empty property object", () => {
const properties = new ParagraphProperties();
expect(() => new Formatter().format(properties)).to.throw("XMLComponent did not format correctly");
});
it("should create", () => {
const properties = new ParagraphProperties({
numbering: {
reference: "test-reference",
level: 0,
instance: 0,
},
});
const tree = new Formatter().format(properties, {
// tslint:disable-next-line: no-object-literal-type-assertion
file: {
Numbering: {
createConcreteNumberingInstance: (_: string, __: number) => {
return;
},
},
} as File,
// tslint:disable-next-line: no-object-literal-type-assertion
viewWrapper: new DocumentWrapper({ background: {} }),
});
expect(tree).to.deep.equal({
"w:pPr": [
{
"w:pStyle": {
_attr: {
"w:val": "ListParagraph",
},
},
},
{
"w:numPr": [
{
"w:ilvl": {
_attr: {
"w:val": 0,
},
},
},
{
"w:numId": {
_attr: {
"w:val": "{test-reference-0}",
},
},
},
],
},
],
});
});
});
});

View File

@ -1,5 +1,6 @@
// http://officeopenxml.com/WPparagraphProperties.php
import { IgnoreIfEmptyXmlComponent, XmlComponent } from "file/xml-components";
import { IContext, IgnoreIfEmptyXmlComponent, IXmlableObject, XmlComponent } from "file/xml-components";
import { DocumentWrapper } from "../document-wrapper";
import { ShadingType } from "../table/shading";
import { Alignment, AlignmentType } from "./formatting/alignment";
import { Bidirectional } from "./formatting/bidirectional";
@ -44,6 +45,7 @@ export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOp
readonly numbering?: {
readonly reference: string;
readonly level: number;
readonly instance?: number;
readonly custom?: boolean;
};
readonly shading?: {
@ -54,6 +56,8 @@ export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOp
}
export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
private readonly numberingReferences: { readonly reference: string; readonly instance: number }[] = [];
constructor(options?: IParagraphPropertiesOptions) {
super("w:pPr");
@ -128,7 +132,12 @@ export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
if (!options.numbering.custom) {
this.push(new Style("ListParagraph"));
}
this.push(new NumberProperties(options.numbering.reference, options.numbering.level));
this.numberingReferences.push({
reference: options.numbering.reference,
instance: options.numbering.instance ?? 0,
});
this.push(new NumberProperties(`${options.numbering.reference}-${options.numbering.instance ?? 0}`, options.numbering.level));
}
if (options.rightTabStop) {
@ -147,4 +156,14 @@ export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
public push(item: XmlComponent): void {
this.root.push(item);
}
public prepForXml(context: IContext): IXmlableObject | undefined {
if (context.viewWrapper instanceof DocumentWrapper) {
for (const reference of this.numberingReferences) {
context.file.Numbering.createConcreteNumberingInstance(reference.reference, reference.instance);
}
}
return super.prepForXml(context);
}
}