feat: Added NumberedItemReference (#3042)
* feat: Added NumberedItemReference * Fix linting * ts-ignore until TypeScript upgrade --------- Co-authored-by: Alexander Nortung <alexander.nortung@oakdigital.dk>
This commit is contained in:
@ -16,12 +16,10 @@ const chapter1 = new Paragraph({
|
||||
children: [
|
||||
new Bookmark({
|
||||
id: "anchorForChapter1",
|
||||
children: [
|
||||
new TextRun("Chapter 1"),
|
||||
],
|
||||
children: [new TextRun("Chapter 1")],
|
||||
}),
|
||||
],
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
Then you can create an hyperlink pointing to that bookmark with an `InternalHyperLink`:
|
||||
@ -35,20 +33,32 @@ const link = new InternalHyperlink({
|
||||
}),
|
||||
],
|
||||
anchor: "anchorForChapter1",
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
### Page reference
|
||||
|
||||
You can also get the page number of the bookmark by creating a page reference to it:
|
||||
|
||||
```ts
|
||||
const paragraph = new Paragraph({
|
||||
children: [
|
||||
new TextRun("Chapter 1 can be seen on page "),
|
||||
new PageReference("anchorForChapter1"),
|
||||
],
|
||||
children: [new TextRun("Chapter 1 can be seen on page "), new PageReference("anchorForChapter1")],
|
||||
});
|
||||
```
|
||||
|
||||
### Numbered item reference
|
||||
|
||||
You can also create cross references for numbered items with `NumberedItemReference`.
|
||||
|
||||
```ts
|
||||
const paragraph = new Paragraph({
|
||||
children: [new TextRun("See Paragraph "), new NumberedItemReference("anchorForParagraph1", "1.1")],
|
||||
});
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> The `NumberedItemReference` currently needs a cached value (in this case `1.1`)
|
||||
|
||||
## External
|
||||
|
||||
To create an external hyperlink you just need to specify the url and the text of the link, then add it to a paragraph:
|
||||
@ -69,7 +79,6 @@ const paragraph = new Paragraph({
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Styling hyperlinks
|
||||
|
||||
It is possible to set the style of the text of both internal and external hyperlinks. This can be done applying run formatting on any of the `TextRun` children of the hyperlink. Use the `style: "Hyperlink"` property to show the default link styles, which can be combined with any other style.
|
||||
|
133
src/file/paragraph/links/numbered-item-ref.spec.ts
Normal file
133
src/file/paragraph/links/numbered-item-ref.spec.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { Formatter } from "@export/formatter";
|
||||
|
||||
import { NumberedItemReference, NumberedItemReferenceFormat } from "./numbered-item-ref";
|
||||
|
||||
describe("NumberedItemReference", () => {
|
||||
describe("#constructor()", () => {
|
||||
it("should create a numbered item ref without options", () => {
|
||||
const ref = new NumberedItemReference("some_bookmark");
|
||||
const tree = new Formatter().format(ref);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:fldSimple": {
|
||||
_attr: {
|
||||
"w:instr": "REF some_bookmark \\h \\w",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a numbered item ref with hyperlink option disabled", () => {
|
||||
const ref = new NumberedItemReference("some_bookmark", "1", { hyperlink: false });
|
||||
const tree = new Formatter().format(ref);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:fldSimple": [
|
||||
{
|
||||
_attr: {
|
||||
"w:instr": "REF some_bookmark \\w",
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"1",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should create a numbered item ref with referenceFormat option", () => {
|
||||
const ref = new NumberedItemReference("some_bookmark", "1", { referenceFormat: NumberedItemReferenceFormat.RELATIVE });
|
||||
const tree = new Formatter().format(ref);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:fldSimple": [
|
||||
{
|
||||
_attr: {
|
||||
"w:instr": "REF some_bookmark \\h \\r",
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"1",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should be possible to use the none referenceFormat option", () => {
|
||||
const ref = new NumberedItemReference("some_bookmark", "1", { referenceFormat: NumberedItemReferenceFormat.NONE });
|
||||
const tree = new Formatter().format(ref);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:fldSimple": [
|
||||
{
|
||||
_attr: {
|
||||
"w:instr": "REF some_bookmark \\h",
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"1",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should be possible to use the NO_CONTEXT referenceFormat option", () => {
|
||||
const ref = new NumberedItemReference("some_bookmark", "1", { referenceFormat: NumberedItemReferenceFormat.NO_CONTEXT });
|
||||
const tree = new Formatter().format(ref);
|
||||
expect(tree).to.deep.equal({
|
||||
"w:fldSimple": [
|
||||
{
|
||||
_attr: {
|
||||
"w:instr": "REF some_bookmark \\h \\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
"w:r": [
|
||||
{
|
||||
"w:t": [
|
||||
{
|
||||
_attr: {
|
||||
"xml:space": "preserve",
|
||||
},
|
||||
},
|
||||
"1",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
66
src/file/paragraph/links/numbered-item-ref.ts
Normal file
66
src/file/paragraph/links/numbered-item-ref.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { SimpleField } from "../run";
|
||||
|
||||
// https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oi29500/7088a8ce-e784-49d4-94b8-cba6ef8fce78
|
||||
export enum NumberedItemReferenceFormat {
|
||||
NONE = "none",
|
||||
/**
|
||||
* \r option - inserts the paragraph number of the bookmarked paragraph in relative context, or relative to its position in the numbering scheme
|
||||
*/
|
||||
RELATIVE = "relative",
|
||||
/**
|
||||
* \n option - causes the field result to be the paragraph number without trailing periods. No information about prior numbered levels is displayed unless it is included as part of the current level.
|
||||
*/
|
||||
NO_CONTEXT = "no_context",
|
||||
/**
|
||||
* \w option - causes the field result to be the entire paragraph number without trailing periods, regardless of the location of the REF field.
|
||||
*/
|
||||
FULL_CONTEXT = "full_context",
|
||||
}
|
||||
|
||||
export type INumberedItemReferenceOptions = {
|
||||
/**
|
||||
* \h option - Creates a hyperlink to the bookmarked paragraph.
|
||||
* @default true
|
||||
*/
|
||||
readonly hyperlink?: boolean;
|
||||
/**
|
||||
* which switch to use for the reference format
|
||||
* @default NumberedItemReferenceFormat.FULL_CONTEXT
|
||||
*/
|
||||
readonly referenceFormat?: NumberedItemReferenceFormat;
|
||||
};
|
||||
|
||||
type Switch = "\\h" | "\\r" | "\\n" | "\\w";
|
||||
|
||||
const SWITCH_MAP: Record<NumberedItemReferenceFormat, Switch | undefined> = {
|
||||
[NumberedItemReferenceFormat.RELATIVE]: "\\r",
|
||||
[NumberedItemReferenceFormat.NO_CONTEXT]: "\\n",
|
||||
[NumberedItemReferenceFormat.FULL_CONTEXT]: "\\w",
|
||||
[NumberedItemReferenceFormat.NONE]: undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a field/cross reference to a numbered item in the document.
|
||||
*/
|
||||
export class NumberedItemReference extends SimpleField {
|
||||
public constructor(
|
||||
bookmarkId: string,
|
||||
// TODO: It would be nice if the cached value could be automatically generated
|
||||
/**
|
||||
* The cached value of the field. This is used to display the field result in the document.
|
||||
*/
|
||||
cachedValue?: string,
|
||||
options: INumberedItemReferenceOptions = {},
|
||||
) {
|
||||
const { hyperlink = true, referenceFormat = NumberedItemReferenceFormat.FULL_CONTEXT } = options;
|
||||
const baseInstruction = `REF ${bookmarkId}`;
|
||||
|
||||
// TODO: Requires TypeScript 5.5 update for it to understand `filter`
|
||||
// @ts-expect-error TS2322
|
||||
const switches: readonly Switch[] = [...(hyperlink ? (["\\h"] as const) : []), ...[SWITCH_MAP[referenceFormat]].filter((a) => !!a)];
|
||||
|
||||
const instruction = `${baseInstruction} ${switches.join(" ")}`;
|
||||
|
||||
super(instruction, cachedValue);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user