Skip to content

Commit f5668e6

Browse files
committed
feat(table-of-contents): cached data
1 parent b064fc5 commit f5668e6

File tree

2 files changed

+104
-8
lines changed

2 files changed

+104
-8
lines changed

demo/28-table-of-contents.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,35 @@ const doc = new File({
3030
sections: [
3131
{
3232
children: [
33-
new TableOfContents("Summary", {
34-
hyperlink: true,
35-
headingStyleRange: "1-5",
36-
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
37-
}),
33+
new TableOfContents(
34+
"Summary",
35+
{
36+
hyperlink: true,
37+
headingStyleRange: "1-5",
38+
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
39+
},
40+
[
41+
{
42+
title: "Header #1",
43+
level: 1,
44+
page: 1,
45+
},
46+
{
47+
title: "Header #2",
48+
level: 1,
49+
page: 2,
50+
},
51+
{
52+
title: "Header #2.1",
53+
level: 2,
54+
},
55+
{
56+
title: "My Spectacular Style #1",
57+
level: 1,
58+
page: 3,
59+
},
60+
],
61+
),
3862
new Paragraph({
3963
text: "Header #1",
4064
heading: HeadingLevel.HEADING_1,
Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,80 @@
11
// http://officeopenxml.com/WPtableOfContents.php
22
// http://www.datypic.com/sc/ooxml/e-w_sdt-1.html
33
import { FileChild } from "@file/file-child";
4-
import { Paragraph } from "@file/paragraph";
5-
import { Run } from "@file/paragraph/run";
4+
import { InternalHyperlink, Paragraph, TabStopDefinition } from "@file/paragraph";
5+
import { Run, Tab } from "@file/paragraph/run";
66
import { Begin, End, Separate } from "@file/paragraph/run/field";
7+
import { Text } from "@file/paragraph/run/run-components/text";
78

89
import { FieldInstruction } from "./field-instruction";
910
import { StructuredDocumentTagContent } from "./sdt-content";
1011
import { StructuredDocumentTagProperties } from "./sdt-properties";
1112
import { ITableOfContentsOptions } from "./table-of-contents-properties";
1213

14+
type ToCEntry = {
15+
readonly title: string;
16+
readonly level: number;
17+
readonly page?: number;
18+
readonly href?: string;
19+
};
20+
1321
export class TableOfContents extends FileChild {
14-
public constructor(alias: string = "Table of Contents", properties?: ITableOfContentsOptions) {
22+
public constructor(alias: string = "Table of Contents", properties?: ITableOfContentsOptions, cachedContent: readonly ToCEntry[] = []) {
1523
super("w:sdt");
1624
this.root.push(new StructuredDocumentTagProperties(alias));
1725

1826
const content = new StructuredDocumentTagContent();
1927

28+
const beginParagraphChildren =
29+
cachedContent.length >= 1 ? [this.buildCachedContentParagraphChild(cachedContent[0], properties)] : [];
30+
31+
const getTabStops = (level: number): readonly TabStopDefinition[] => {
32+
const levelSpace = 720;
33+
const levelPosition = 9025 - (level - 1) * levelSpace; // TODO: should be equal to page width - level margin
34+
return [
35+
{
36+
type: "clear",
37+
position: levelPosition,
38+
},
39+
{
40+
type: "right",
41+
position: 9025, // TODO: should be equal to page width
42+
leader: "dot",
43+
},
44+
];
45+
};
46+
2047
const beginParagraph = new Paragraph({
48+
style: cachedContent.length >= 1 ? `TOC${cachedContent[0].level}` : undefined,
49+
tabStops: getTabStops(cachedContent.length >= 1 ? cachedContent[0].level : 1),
2150
children: [
2251
new Run({
2352
children: [new Begin(true), new FieldInstruction(properties), new Separate()],
2453
}),
54+
...beginParagraphChildren,
2555
],
2656
});
2757

2858
content.addChildElement(beginParagraph);
2959

60+
const middleParagraphs = cachedContent.slice(1, -1).map((entry) => new Paragraph({
61+
style: `TOC${entry.level}`,
62+
tabStops: getTabStops(entry.level),
63+
children: [this.buildCachedContentParagraphChild(entry, properties)],
64+
}));
65+
66+
for (const paragraph of middleParagraphs) {
67+
content.addChildElement(paragraph);
68+
}
69+
70+
const endParagraphChildren =
71+
cachedContent.length > 1 ? [this.buildCachedContentParagraphChild(cachedContent[cachedContent.length - 1], properties)] : [];
72+
3073
const endParagraph = new Paragraph({
74+
style: cachedContent.length > 1 ? `TOC${cachedContent[cachedContent.length - 1].level}` : undefined,
75+
tabStops: getTabStops(cachedContent.length > 1 ? cachedContent[cachedContent.length - 1].level : 1),
3176
children: [
77+
...endParagraphChildren,
3278
new Run({
3379
children: [new End()],
3480
}),
@@ -39,4 +85,30 @@ export class TableOfContents extends FileChild {
3985

4086
this.root.push(content);
4187
}
88+
89+
private buildCachedContentRun(entry: ToCEntry): Run {
90+
return new Run({
91+
children: [
92+
new Text({
93+
text: entry.title,
94+
}),
95+
new Tab(),
96+
new Text({
97+
text: entry.page?.toString() ?? "",
98+
}),
99+
],
100+
});
101+
}
102+
103+
private buildCachedContentParagraphChild(entry: ToCEntry, properties?: ITableOfContentsOptions): Run | InternalHyperlink {
104+
const run = this.buildCachedContentRun(entry);
105+
if (properties?.hyperlink && entry.href !== undefined) {
106+
return new InternalHyperlink({
107+
anchor: entry.href,
108+
children: [run],
109+
});
110+
}
111+
112+
return run;
113+
}
42114
}

0 commit comments

Comments
 (0)