Merge pull request #1448 from chenyuncai/master
Implement sample version of Comment feature.
This commit is contained in:
8
.nycrc
8
.nycrc
@ -1,9 +1,9 @@
|
||||
{
|
||||
"check-coverage": true,
|
||||
"statements": 99.32,
|
||||
"branches": 96.27,
|
||||
"functions": 99.11,
|
||||
"lines": 99.32,
|
||||
"statements": 99.43,
|
||||
"branches": 96.6,
|
||||
"functions": 99.47,
|
||||
"lines": 99.43,
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
|
36
demo/73-comments.ts
Normal file
36
demo/73-comments.ts
Normal file
@ -0,0 +1,36 @@
|
||||
// Simple example to add comments to a document
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph, TextRun, CommentRangeStart, CommentRangeEnd, CommentReference } from "../build";
|
||||
|
||||
const doc = new Document({
|
||||
comments: {
|
||||
children: [{ id: 0, author: "Ray Chen", date: new Date(), text: "comment text content" }],
|
||||
},
|
||||
sections: [
|
||||
{
|
||||
properties: {},
|
||||
children: [
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun("Hello World"),
|
||||
new CommentRangeStart(0),
|
||||
new TextRun({
|
||||
text: "Foo Bar",
|
||||
bold: true,
|
||||
}),
|
||||
new CommentRangeEnd(0),
|
||||
new TextRun({
|
||||
children: [new CommentReference(0)],
|
||||
bold: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
17
docs/usage/comments.md
Normal file
17
docs/usage/comments.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Comments
|
||||
|
||||
!> Comments requires an understanding of [Sections](usage/sections.md) and [Paragraphs](usage/paragraph.md).
|
||||
|
||||
## Intro
|
||||
|
||||
To add comments in `docx`, a `comments` block is specified in the `Document`. This block defines all the comments in your document. Each comment has an `id`, which you then reference later.
|
||||
|
||||
In the spot you want to add a comment, you simply add a `CommentRangeStart` and a `CommentRangeEnd` to specify where the comment starts and ends.
|
||||
|
||||
Alternatively, you can use `CommentReference` to specify a comment at a specific singular point.
|
||||
|
||||
### Example
|
||||
|
||||
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/73-comments.ts ':include')
|
||||
|
||||
_Source: https://github.com/dolanmiu/docx/blob/master/demo/73-comments.ts_
|
@ -18,12 +18,15 @@ describe("Compiler", () => {
|
||||
this.timeout(99999999);
|
||||
const file = new File({
|
||||
sections: [],
|
||||
comments: {
|
||||
children: [],
|
||||
},
|
||||
});
|
||||
const zipFile = compiler.compile(file);
|
||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||
|
||||
expect(fileNames).is.an.instanceof(Array);
|
||||
expect(fileNames).has.length(16);
|
||||
expect(fileNames).has.length(17);
|
||||
expect(fileNames).to.include("word/document.xml");
|
||||
expect(fileNames).to.include("word/styles.xml");
|
||||
expect(fileNames).to.include("docProps/core.xml");
|
||||
@ -33,6 +36,7 @@ describe("Compiler", () => {
|
||||
expect(fileNames).to.include("word/footnotes.xml");
|
||||
expect(fileNames).to.include("word/_rels/footnotes.xml.rels");
|
||||
expect(fileNames).to.include("word/settings.xml");
|
||||
expect(fileNames).to.include("word/comments.xml");
|
||||
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
||||
expect(fileNames).to.include("[Content_Types].xml");
|
||||
expect(fileNames).to.include("_rels/.rels");
|
||||
@ -76,7 +80,7 @@ describe("Compiler", () => {
|
||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||
|
||||
expect(fileNames).is.an.instanceof(Array);
|
||||
expect(fileNames).has.length(24);
|
||||
expect(fileNames).has.length(25);
|
||||
|
||||
expect(fileNames).to.include("word/header1.xml");
|
||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||
|
@ -5,6 +5,7 @@ import { File } from "file";
|
||||
import { Formatter } from "../formatter";
|
||||
import { ImageReplacer } from "./image-replacer";
|
||||
import { NumberingReplacer } from "./numbering-replacer";
|
||||
import { PrettityType } from "./packer";
|
||||
|
||||
interface IXmlifyedFile {
|
||||
readonly data: string;
|
||||
@ -28,6 +29,7 @@ interface IXmlifyedFileMapping {
|
||||
readonly FootNotes: IXmlifyedFile;
|
||||
readonly FootNotesRelationships: IXmlifyedFile;
|
||||
readonly Settings: IXmlifyedFile;
|
||||
readonly Comments?: IXmlifyedFile;
|
||||
}
|
||||
|
||||
export class Compiler {
|
||||
@ -41,7 +43,7 @@ export class Compiler {
|
||||
this.numberingReplacer = new NumberingReplacer();
|
||||
}
|
||||
|
||||
public compile(file: File, prettifyXml?: boolean): JSZip {
|
||||
public compile(file: File, prettifyXml?: boolean | PrettityType): JSZip {
|
||||
const zip = new JSZip();
|
||||
const xmlifiedFileMapping = this.xmlifyFile(file, prettifyXml);
|
||||
const map = new Map<string, IXmlifyedFile | IXmlifyedFile[]>(Object.entries(xmlifiedFileMapping));
|
||||
@ -64,7 +66,7 @@ export class Compiler {
|
||||
return zip;
|
||||
}
|
||||
|
||||
private xmlifyFile(file: File, prettify?: boolean): IXmlifyedFileMapping {
|
||||
private xmlifyFile(file: File, prettify?: boolean | PrettityType): IXmlifyedFileMapping {
|
||||
const documentRelationshipCount = file.Document.Relationships.RelationshipCount + 1;
|
||||
|
||||
const documentXmlData = xml(
|
||||
@ -112,7 +114,6 @@ export class Compiler {
|
||||
data: (() => {
|
||||
const xmlData = this.imageReplacer.replace(documentXmlData, documentMediaDatas, documentRelationshipCount);
|
||||
const referenedXmlData = this.numberingReplacer.replace(xmlData, file.Numbering.ConcreteNumbering);
|
||||
|
||||
return referenedXmlData;
|
||||
})(),
|
||||
path: "word/document.xml",
|
||||
@ -403,6 +404,29 @@ export class Compiler {
|
||||
),
|
||||
path: "word/settings.xml",
|
||||
},
|
||||
Comments: {
|
||||
data: (() => {
|
||||
if (!file.Comments) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = xml(
|
||||
this.formatter.format(file.Comments, {
|
||||
viewWrapper: file.Document,
|
||||
file,
|
||||
}),
|
||||
{
|
||||
indent: prettify,
|
||||
declaration: {
|
||||
standalone: "yes",
|
||||
encoding: "UTF-8",
|
||||
},
|
||||
},
|
||||
);
|
||||
return data;
|
||||
})(),
|
||||
path: "word/comments.xml",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,18 @@
|
||||
import { File } from "file";
|
||||
import { Compiler } from "./next-compiler";
|
||||
|
||||
/**
|
||||
* Use blanks to prettify
|
||||
*/
|
||||
export enum PrettityType {
|
||||
NONE = "",
|
||||
WITH_2_BLANKS = " ",
|
||||
WITH_4_BLANKS = " ",
|
||||
WITH_TAB = "\t",
|
||||
}
|
||||
|
||||
export class Packer {
|
||||
public static async toBuffer(file: File, prettify?: boolean): Promise<Buffer> {
|
||||
public static async toBuffer(file: File, prettify?: boolean | PrettityType): Promise<Buffer> {
|
||||
const zip = this.compiler.compile(file, prettify);
|
||||
const zipData = await zip.generateAsync({
|
||||
type: "nodebuffer",
|
||||
@ -13,7 +23,7 @@ export class Packer {
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public static async toBase64String(file: File, prettify?: boolean): Promise<string> {
|
||||
public static async toBase64String(file: File, prettify?: boolean | PrettityType): Promise<string> {
|
||||
const zip = this.compiler.compile(file, prettify);
|
||||
const zipData = await zip.generateAsync({
|
||||
type: "base64",
|
||||
@ -24,7 +34,7 @@ export class Packer {
|
||||
return zipData;
|
||||
}
|
||||
|
||||
public static async toBlob(file: File, prettify?: boolean): Promise<Blob> {
|
||||
public static async toBlob(file: File, prettify?: boolean | PrettityType): Promise<Blob> {
|
||||
const zip = this.compiler.compile(file, prettify);
|
||||
const zipData = await zip.generateAsync({
|
||||
type: "blob",
|
||||
|
@ -102,7 +102,7 @@ describe("ContentTypes", () => {
|
||||
contentTypes.addFooter(102);
|
||||
const tree = new Formatter().format(contentTypes);
|
||||
|
||||
expect(tree["Types"][16]).to.deep.equal({
|
||||
expect(tree["Types"][17]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
@ -111,7 +111,7 @@ describe("ContentTypes", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree["Types"][17]).to.deep.equal({
|
||||
expect(tree["Types"][18]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||
@ -128,7 +128,7 @@ describe("ContentTypes", () => {
|
||||
contentTypes.addHeader(202);
|
||||
const tree = new Formatter().format(contentTypes);
|
||||
|
||||
expect(tree["Types"][16]).to.deep.equal({
|
||||
expect(tree["Types"][17]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
@ -137,7 +137,7 @@ describe("ContentTypes", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(tree["Types"][17]).to.deep.equal({
|
||||
expect(tree["Types"][18]).to.deep.equal({
|
||||
Override: {
|
||||
_attr: {
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||
|
@ -32,6 +32,7 @@ export class ContentTypes extends XmlComponent {
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", "/word/settings.xml"));
|
||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml", "/word/comments.xml"));
|
||||
}
|
||||
|
||||
public addFooter(index: number): void {
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { ICommentsOptions } from "file/paragraph/run/comment-run";
|
||||
import { StringContainer, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { ICustomPropertyOptions } from "../custom-properties";
|
||||
import { IDocumentBackgroundOptions } from "../document";
|
||||
|
||||
@ -21,6 +23,7 @@ export interface IPropertiesOptions {
|
||||
readonly externalStyles?: string;
|
||||
readonly styles?: IStylesOptions;
|
||||
readonly numbering?: INumberingOptions;
|
||||
readonly comments?: ICommentsOptions;
|
||||
readonly footnotes?: {
|
||||
readonly [key: string]: {
|
||||
readonly children: Paragraph[];
|
||||
|
@ -24,6 +24,22 @@ export interface IDocumentAttributesProperties {
|
||||
readonly dcmitype?: string;
|
||||
readonly xsi?: string;
|
||||
readonly type?: string;
|
||||
readonly cx?: string;
|
||||
readonly cx1?: string;
|
||||
readonly cx2?: string;
|
||||
readonly cx3?: string;
|
||||
readonly cx4?: string;
|
||||
readonly cx5?: string;
|
||||
readonly cx6?: string;
|
||||
readonly cx7?: string;
|
||||
readonly cx8?: string;
|
||||
readonly aink?: string;
|
||||
readonly am3d?: string;
|
||||
readonly w16cex?: string;
|
||||
readonly w16cid?: string;
|
||||
readonly w16?: string;
|
||||
readonly w16sdtdh?: string;
|
||||
readonly w16se?: string;
|
||||
}
|
||||
|
||||
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
|
||||
@ -51,5 +67,21 @@ export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttribute
|
||||
dcmitype: "xmlns:dcmitype",
|
||||
xsi: "xmlns:xsi",
|
||||
type: "xsi:type",
|
||||
cx: "xmlns:cx",
|
||||
cx1: "xmlns:cx1",
|
||||
cx2: "xmlns:cx2",
|
||||
cx3: "xmlns:cx3",
|
||||
cx4: "xmlns:cx4",
|
||||
cx5: "xmlns:cx5",
|
||||
cx6: "xmlns:cx6",
|
||||
cx7: "xmlns:cx7",
|
||||
cx8: "xmlns:cx8",
|
||||
aink: "xmlns:aink",
|
||||
am3d: "xmlns:am3d",
|
||||
w16cex: "xmlns:w16cex",
|
||||
w16cid: "xmlns:w16cid",
|
||||
w16: "xmlns:w16",
|
||||
w16sdtdh: "xmlns:w16sdtdh",
|
||||
w16se: "xmlns:w16se",
|
||||
};
|
||||
}
|
||||
|
@ -31,11 +31,27 @@ describe("Document", () => {
|
||||
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
"xmlns:w16": "http://schemas.microsoft.com/office/word/2018/wordml",
|
||||
"xmlns:w16cex": "http://schemas.microsoft.com/office/word/2018/wordml/cex",
|
||||
"xmlns:w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||
"xmlns:w16sdtdh": "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
|
||||
"xmlns:w16se": "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
"mc:Ignorable": "w14 w15 wp14",
|
||||
"xmlns:aink": "http://schemas.microsoft.com/office/drawing/2016/ink",
|
||||
"xmlns:am3d": "http://schemas.microsoft.com/office/drawing/2017/model3d",
|
||||
"xmlns:cx": "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||
"xmlns:cx1": "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||
"xmlns:cx2": "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||
"xmlns:cx3": "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||
"xmlns:cx4": "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||
"xmlns:cx5": "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||
"xmlns:cx6": "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||
"xmlns:cx7": "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||
"xmlns:cx8": "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -53,6 +53,22 @@ export class Document extends XmlComponent {
|
||||
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||
cx1: "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||
cx2: "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||
cx3: "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||
cx4: "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||
cx5: "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||
cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||
cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||
cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||
aink: "http://schemas.microsoft.com/office/drawing/2016/ink",
|
||||
am3d: "http://schemas.microsoft.com/office/drawing/2017/model3d",
|
||||
w16cex: "http://schemas.microsoft.com/office/word/2018/wordml/cex",
|
||||
w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||
w16: "http://schemas.microsoft.com/office/word/2018/wordml",
|
||||
w16sdtdh: "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
|
||||
w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||
Ignorable: "w14 w15 wp14",
|
||||
}),
|
||||
);
|
||||
|
@ -387,4 +387,30 @@ describe("File", () => {
|
||||
|
||||
expect(tree["w:settings"][2]).to.deep.equal({ "w:evenAndOddHeaders": {} });
|
||||
});
|
||||
|
||||
describe("#comments", () => {
|
||||
it("should create comments", () => {
|
||||
const doc = new File({
|
||||
comments: {
|
||||
children: [],
|
||||
},
|
||||
sections: [],
|
||||
});
|
||||
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
expect(doc.Comments).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe("#numbering", () => {
|
||||
it("should create", () => {
|
||||
const doc = new File({
|
||||
numbering: { config: [] },
|
||||
sections: [],
|
||||
});
|
||||
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
expect(doc.Numbering).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -12,6 +12,7 @@ import { HeaderWrapper, IDocumentHeader } from "./header-wrapper";
|
||||
import { Media } from "./media";
|
||||
import { Numbering } from "./numbering";
|
||||
import { Paragraph } from "./paragraph";
|
||||
import { Comments } from "./paragraph/run/comment-run";
|
||||
import { Relationships } from "./relationships";
|
||||
import { Settings } from "./settings";
|
||||
import { Styles } from "./styles";
|
||||
@ -52,6 +53,7 @@ export class File {
|
||||
private readonly customProperties: CustomProperties;
|
||||
private readonly appProperties: AppProperties;
|
||||
private readonly styles: Styles;
|
||||
private readonly comments: Comments;
|
||||
|
||||
constructor(options: IPropertiesOptions, fileProperties: IFileProperties = {}) {
|
||||
this.coreProperties = new CoreProperties({
|
||||
@ -69,6 +71,10 @@ export class File {
|
||||
},
|
||||
);
|
||||
|
||||
if (options.comments) {
|
||||
this.comments = new Comments(options.comments);
|
||||
}
|
||||
|
||||
this.fileRelationships = new Relationships();
|
||||
this.customProperties = new CustomProperties(options.customProperties ?? []);
|
||||
this.appProperties = new AppProperties();
|
||||
@ -92,7 +98,7 @@ export class File {
|
||||
if (fileProperties.template && options.externalStyles) {
|
||||
throw Error("can not use both template and external styles");
|
||||
}
|
||||
if (fileProperties.template) {
|
||||
if (fileProperties.template && fileProperties.template.styles) {
|
||||
const stylesFactory = new ExternalStylesFactory();
|
||||
this.styles = stylesFactory.newInstance(fileProperties.template.styles);
|
||||
} else if (options.externalStyles) {
|
||||
@ -240,6 +246,11 @@ export class File {
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
|
||||
"settings.xml",
|
||||
);
|
||||
this.documentWrapper.Relationships.createRelationship(
|
||||
this.currentRelationshipId++,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
||||
"comments.xml",
|
||||
);
|
||||
}
|
||||
|
||||
public get Document(): DocumentWrapper {
|
||||
@ -293,4 +304,8 @@ export class File {
|
||||
public get Settings(): Settings {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
public get Comments(): Comments {
|
||||
return this.comments;
|
||||
}
|
||||
}
|
||||
|
42
src/file/paragraph/formatting/indent.spec.ts
Normal file
42
src/file/paragraph/formatting/indent.spec.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
|
||||
import { Indent } from "./indent";
|
||||
|
||||
describe("Indent", () => {
|
||||
it("should create", () => {
|
||||
const indent = new Indent({
|
||||
start: 10,
|
||||
end: 10,
|
||||
left: 10,
|
||||
right: 10,
|
||||
hanging: 10,
|
||||
firstLine: 10,
|
||||
});
|
||||
const tree = new Formatter().format(indent);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:ind": {
|
||||
_attr: {
|
||||
"w:start": 10,
|
||||
"w:end": 10,
|
||||
"w:firstLine": 10,
|
||||
"w:hanging": 10,
|
||||
"w:left": 10,
|
||||
"w:right": 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should create with no indent values", () => {
|
||||
const indent = new Indent({});
|
||||
|
||||
const tree = new Formatter().format(indent);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:ind": {
|
||||
_attr: {},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
// http://officeopenxml.com/WPparagraph.php
|
||||
|
||||
import { uniqueId } from "convenience-functions";
|
||||
import { FootnoteReferenceRun } from "file/footnotes/footnote/run/reference-run";
|
||||
import { IContext, IXmlableObject, XmlComponent } from "file/xml-components";
|
||||
@ -11,6 +10,7 @@ import { Bookmark, ConcreteHyperlink, ExternalHyperlink, InternalHyperlink } fro
|
||||
import { Math } from "./math";
|
||||
import { IParagraphPropertiesOptions, ParagraphProperties } from "./properties";
|
||||
import { ImageRun, Run, SequentialIdentifier, SimpleField, SimpleMailMergeField, SymbolRun, TextRun } from "./run";
|
||||
import { Comment, CommentRangeEnd, CommentRangeStart, CommentReference, Comments } from "./run/comment-run";
|
||||
|
||||
export type ParagraphChild =
|
||||
| TextRun
|
||||
@ -27,7 +27,12 @@ export type ParagraphChild =
|
||||
| DeletedTextRun
|
||||
| Math
|
||||
| SimpleField
|
||||
| SimpleMailMergeField;
|
||||
| SimpleMailMergeField
|
||||
| Comments
|
||||
| Comment
|
||||
| CommentRangeStart
|
||||
| CommentRangeEnd
|
||||
| CommentReference;
|
||||
|
||||
export interface IParagraphOptions extends IParagraphPropertiesOptions {
|
||||
readonly text?: string;
|
||||
|
150
src/file/paragraph/run/comment-run.spec.ts
Normal file
150
src/file/paragraph/run/comment-run.spec.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "export/formatter";
|
||||
import { Comment, CommentRangeEnd, CommentRangeStart, CommentReference, Comments } from "./comment-run";
|
||||
|
||||
describe("CommentRangeStart", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create", () => {
|
||||
const component = new CommentRangeStart(0);
|
||||
const tree = new Formatter().format(component);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:commentRangeStart": { _attr: { "w:id": 0 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("CommentRangeEnd", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create", () => {
|
||||
const component = new CommentRangeEnd(0);
|
||||
const tree = new Formatter().format(component);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:commentRangeEnd": { _attr: { "w:id": 0 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("CommentReference", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create", () => {
|
||||
const component = new CommentReference(0);
|
||||
const tree = new Formatter().format(component);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:commentReference": { _attr: { "w:id": 0 } },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Comment", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create", () => {
|
||||
const component = new Comment({
|
||||
id: 0,
|
||||
text: "test-comment",
|
||||
date: new Date(1999, 0, 1),
|
||||
});
|
||||
const tree = new Formatter().format(component);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:comment": [
|
||||
{ _attr: { "w:id": 0, "w:date": "1999-01-01T00:00:00.000Z" } },
|
||||
{
|
||||
"w:p": [
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"test-comment",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Comments", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create", () => {
|
||||
const component = new Comments({
|
||||
children: [
|
||||
{
|
||||
id: 0,
|
||||
text: "test-comment",
|
||||
date: new Date(1999, 0, 1),
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
text: "test-comment-2",
|
||||
date: new Date(1999, 0, 1),
|
||||
},
|
||||
],
|
||||
});
|
||||
const tree = new Formatter().format(component);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:comments": [
|
||||
{
|
||||
_attr: {
|
||||
"xmlns:cx": "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||
"xmlns:cx1": "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||
"xmlns:cx2": "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||
"xmlns:cx3": "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||
"xmlns:cx4": "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||
"xmlns:cx5": "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||
"xmlns:cx6": "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||
"xmlns:cx7": "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||
"xmlns:cx8": "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
"xmlns:aink": "http://schemas.microsoft.com/office/drawing/2016/ink",
|
||||
"xmlns:am3d": "http://schemas.microsoft.com/office/drawing/2017/model3d",
|
||||
"xmlns:o": "urn:schemas-microsoft-com:office:office",
|
||||
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||
"xmlns:v": "urn:schemas-microsoft-com:vml",
|
||||
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
|
||||
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
"xmlns:w16cex": "http://schemas.microsoft.com/office/word/2018/wordml/cex",
|
||||
"xmlns:w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||
"xmlns:w16": "http://schemas.microsoft.com/office/word/2018/wordml",
|
||||
"xmlns:w16sdtdh": "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
|
||||
"xmlns:w16se": "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:comment": [
|
||||
{ _attr: { "w:id": 0, "w:date": "1999-01-01T00:00:00.000Z" } },
|
||||
{ "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test-comment"] }] }] },
|
||||
],
|
||||
},
|
||||
{
|
||||
"w:comment": [
|
||||
{ _attr: { "w:id": 1, "w:date": "1999-01-01T00:00:00.000Z" } },
|
||||
{ "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test-comment-2"] }] }] },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
182
src/file/paragraph/run/comment-run.ts
Normal file
182
src/file/paragraph/run/comment-run.ts
Normal file
@ -0,0 +1,182 @@
|
||||
import { Paragraph } from "file/paragraph";
|
||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||
|
||||
import { TextRun } from "./text-run";
|
||||
|
||||
export interface ICommentOptions {
|
||||
readonly id: number;
|
||||
readonly text: string;
|
||||
readonly initials?: string;
|
||||
readonly author?: string;
|
||||
readonly date?: Date;
|
||||
}
|
||||
|
||||
export interface ICommentsOptions {
|
||||
readonly children: ICommentOptions[];
|
||||
}
|
||||
|
||||
class CommentAttributes extends XmlAttributeComponent<{
|
||||
readonly id: number;
|
||||
readonly initials?: string;
|
||||
readonly author?: string;
|
||||
readonly date?: string;
|
||||
}> {
|
||||
protected readonly xmlKeys = { id: "w:id", initials: "w:initials", author: "w:author", date: "w:date" };
|
||||
}
|
||||
|
||||
class CommentRangeAttributes extends XmlAttributeComponent<{ readonly id: number }> {
|
||||
protected readonly xmlKeys = { id: "w:id" };
|
||||
}
|
||||
class RootCommentsAttributes extends XmlAttributeComponent<{
|
||||
readonly "xmlns:cx"?: string;
|
||||
readonly "xmlns:cx1"?: string;
|
||||
readonly "xmlns:cx2"?: string;
|
||||
readonly "xmlns:cx3"?: string;
|
||||
readonly "xmlns:cx4"?: string;
|
||||
readonly "xmlns:cx5"?: string;
|
||||
readonly "xmlns:cx6"?: string;
|
||||
readonly "xmlns:cx7"?: string;
|
||||
readonly "xmlns:cx8"?: string;
|
||||
readonly "xmlns:mc"?: string;
|
||||
readonly "xmlns:aink"?: string;
|
||||
readonly "xmlns:am3d"?: string;
|
||||
readonly "xmlns:o"?: string;
|
||||
readonly "xmlns:r"?: string;
|
||||
readonly "xmlns:m"?: string;
|
||||
readonly "xmlns:v"?: string;
|
||||
readonly "xmlns:wp14"?: string;
|
||||
readonly "xmlns:wp"?: string;
|
||||
readonly "xmlns:w10"?: string;
|
||||
readonly "xmlns:w"?: string;
|
||||
readonly "xmlns:w14"?: string;
|
||||
readonly "xmlns:w15"?: string;
|
||||
readonly "xmlns:w16cex"?: string;
|
||||
readonly "xmlns:w16cid"?: string;
|
||||
readonly "xmlns:w16"?: string;
|
||||
readonly "xmlns:w16sdtdh"?: string;
|
||||
readonly "xmlns:w16se"?: string;
|
||||
readonly "xmlns:wpg": string;
|
||||
readonly "xmlns:wpi"?: string;
|
||||
readonly "xmlns:wne"?: string;
|
||||
readonly "xmlns:wps"?: string;
|
||||
}> {
|
||||
protected readonly xmlKeys = {
|
||||
"xmlns:cx": "xmlns:cx",
|
||||
"xmlns:cx1": "xmlns:cx1",
|
||||
"xmlns:cx2": "xmlns:cx2",
|
||||
"xmlns:cx3": "xmlns:cx3",
|
||||
"xmlns:cx4": "xmlns:cx4",
|
||||
"xmlns:cx5": "xmlns:cx5",
|
||||
"xmlns:cx6": "xmlns:cx6",
|
||||
"xmlns:cx7": "xmlns:cx7",
|
||||
"xmlns:cx8": "xmlns:cx8",
|
||||
"xmlns:mc": "xmlns:mc",
|
||||
"xmlns:aink": "xmlns:aink",
|
||||
"xmlns:am3d": "xmlns:am3d",
|
||||
"xmlns:o": "xmlns:o",
|
||||
"xmlns:r": "xmlns:r",
|
||||
"xmlns:m": "xmlns:m",
|
||||
"xmlns:v": "xmlns:v",
|
||||
"xmlns:wp14": "xmlns:wp14",
|
||||
"xmlns:wp": "xmlns:wp",
|
||||
"xmlns:w10": "xmlns:w10",
|
||||
"xmlns:w": "xmlns:w",
|
||||
"xmlns:w14": "xmlns:w14",
|
||||
"xmlns:w15": "xmlns:w15",
|
||||
"xmlns:w16cex": "xmlns:w16cex",
|
||||
"xmlns:w16cid": "xmlns:w16cid",
|
||||
"xmlns:w16": "xmlns:w16",
|
||||
"xmlns:w16sdtdh": "xmlns:w16sdtdh",
|
||||
"xmlns:w16se": "xmlns:w16se",
|
||||
"xmlns:wpg": "xmlns:wpg",
|
||||
"xmlns:wpi": "xmlns:wpi",
|
||||
"xmlns:wne": "xmlns:wne",
|
||||
"xmlns:wps": "xmlns:wps",
|
||||
};
|
||||
}
|
||||
|
||||
export class CommentRangeStart extends XmlComponent {
|
||||
constructor(id: number) {
|
||||
super("w:commentRangeStart");
|
||||
|
||||
this.root.push(new CommentRangeAttributes({ id }));
|
||||
}
|
||||
}
|
||||
|
||||
export class CommentRangeEnd extends XmlComponent {
|
||||
constructor(id: number) {
|
||||
super("w:commentRangeEnd");
|
||||
|
||||
this.root.push(new CommentRangeAttributes({ id }));
|
||||
}
|
||||
}
|
||||
|
||||
export class CommentReference extends XmlComponent {
|
||||
constructor(id: number) {
|
||||
super("w:commentReference");
|
||||
|
||||
this.root.push(new CommentRangeAttributes({ id }));
|
||||
}
|
||||
}
|
||||
|
||||
export class Comment extends XmlComponent {
|
||||
constructor({ id, initials, author, date = new Date(), text }: ICommentOptions) {
|
||||
super("w:comment");
|
||||
|
||||
this.root.push(
|
||||
new CommentAttributes({
|
||||
id,
|
||||
initials,
|
||||
author,
|
||||
date: date.toISOString(),
|
||||
}),
|
||||
);
|
||||
|
||||
this.root.push(new Paragraph({ children: [new TextRun(text)] }));
|
||||
}
|
||||
}
|
||||
export class Comments extends XmlComponent {
|
||||
constructor({ children }: ICommentsOptions) {
|
||||
super("w:comments");
|
||||
|
||||
this.root.push(
|
||||
new RootCommentsAttributes({
|
||||
"xmlns:cx": "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
||||
"xmlns:cx1": "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
|
||||
"xmlns:cx2": "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
|
||||
"xmlns:cx3": "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
|
||||
"xmlns:cx4": "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
|
||||
"xmlns:cx5": "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
|
||||
"xmlns:cx6": "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
|
||||
"xmlns:cx7": "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
|
||||
"xmlns:cx8": "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
|
||||
"xmlns:mc": "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||
"xmlns:aink": "http://schemas.microsoft.com/office/drawing/2016/ink",
|
||||
"xmlns:am3d": "http://schemas.microsoft.com/office/drawing/2017/model3d",
|
||||
"xmlns:o": "urn:schemas-microsoft-com:office:office",
|
||||
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||
"xmlns:m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
||||
"xmlns:v": "urn:schemas-microsoft-com:vml",
|
||||
"xmlns:wp14": "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
|
||||
"xmlns:wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
||||
"xmlns:w10": "urn:schemas-microsoft-com:office:word",
|
||||
"xmlns:w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||
"xmlns:w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||
"xmlns:w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||
"xmlns:w16cex": "http://schemas.microsoft.com/office/word/2018/wordml/cex",
|
||||
"xmlns:w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||
"xmlns:w16": "http://schemas.microsoft.com/office/word/2018/wordml",
|
||||
"xmlns:w16sdtdh": "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
|
||||
"xmlns:w16se": "http://schemas.microsoft.com/office/word/2015/wordml/symex",
|
||||
"xmlns:wpg": "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
|
||||
"xmlns:wpi": "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
|
||||
"xmlns:wne": "http://schemas.microsoft.com/office/word/2006/wordml",
|
||||
"xmlns:wps": "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
|
||||
}),
|
||||
);
|
||||
|
||||
for (const child of children) {
|
||||
this.root.push(new Comment(child));
|
||||
}
|
||||
}
|
||||
}
|
@ -9,3 +9,4 @@ export * from "./underline";
|
||||
export * from "./emphasis-mark";
|
||||
export * from "./tab";
|
||||
export * from "./simple-field";
|
||||
export * from "./comment-run";
|
||||
|
@ -16,7 +16,8 @@ export type RelationshipType =
|
||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"
|
||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"
|
||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
|
||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"
|
||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
|
||||
|
||||
export enum TargetModeType {
|
||||
EXTERNAL = "External",
|
||||
|
@ -24,8 +24,4 @@ export abstract class XmlAttributeComponent<T> extends BaseXmlComponent {
|
||||
});
|
||||
return { _attr: attrs };
|
||||
}
|
||||
|
||||
public set(properties: T): void {
|
||||
this.root = properties;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user