New Textbox Component (#2718)
* Adding textbox with no props * Fixing namings and adding shape style * simplify usage * Fix linting issues * Re-name demo * Use new shape authoring style * Add tests Simplify API * Add better types for styles * Add more options to TextBox and add documentation --------- Co-authored-by: zohar11 <zohar@sumit-ai.com> Co-authored-by: Dolan <dolan_miu@hotmail.com>
This commit is contained in:
committed by
GitHub
parent
c6bb255641
commit
3654eb0800
43
demo/94-texbox.ts
Normal file
43
demo/94-texbox.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Simple example to add textbox to a document
|
||||||
|
import { Document, Packer, Paragraph, Textbox, TextRun } from "docx";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
properties: {},
|
||||||
|
children: [
|
||||||
|
new Textbox({
|
||||||
|
alignment: "center",
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hi i'm a textbox!")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
style: {
|
||||||
|
width: "200pt",
|
||||||
|
height: "auto",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new Textbox({
|
||||||
|
alignment: "center",
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hi i'm a textbox with a hidden box!")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
style: {
|
||||||
|
width: "300pt",
|
||||||
|
height: 400,
|
||||||
|
visibility: "hidden",
|
||||||
|
zIndex: "auto",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
26
docs/usage/text-box.md
Normal file
26
docs/usage/text-box.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Text Box
|
||||||
|
|
||||||
|
Similar `Text Frames`, but the difference being that it is `VML` `Shape` based.
|
||||||
|
|
||||||
|
!> `Text Boxes` requires an understanding of [Paragraphs](usage/paragraph.md).
|
||||||
|
|
||||||
|
> `Text boxes` are paragraphs of text in a document which are positioned in a separate region or frame in the document, and can be positioned with a specific size and position relative to non-frame paragraphs in the current document.
|
||||||
|
|
||||||
|
## Intro
|
||||||
|
|
||||||
|
To make a `Text Box`, simply create a `Textbox` object inside the `Document`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new Textbox({
|
||||||
|
alignment: "center",
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
children: [new TextRun("Hi i'm a textbox!")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
style: {
|
||||||
|
width: "200pt",
|
||||||
|
height: "auto",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
@ -1,6 +1,6 @@
|
|||||||
# Text Frames
|
# Text Frames
|
||||||
|
|
||||||
Also known as `Text Boxes`
|
> Similar to `Text Boxes`!
|
||||||
|
|
||||||
!> Text Frames requires an understanding of [Paragraphs](usage/paragraph.md).
|
!> Text Frames requires an understanding of [Paragraphs](usage/paragraph.md).
|
||||||
|
|
||||||
|
@ -20,3 +20,4 @@ export * from "./border";
|
|||||||
export * from "./vertical-align";
|
export * from "./vertical-align";
|
||||||
export * from "./checkbox";
|
export * from "./checkbox";
|
||||||
export * from "./fonts";
|
export * from "./fonts";
|
||||||
|
export * from "./textbox";
|
||||||
|
1
src/file/textbox/index.ts
Normal file
1
src/file/textbox/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./textbox";
|
11
src/file/textbox/pict-element/pict-element.ts
Normal file
11
src/file/textbox/pict-element/pict-element.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
export type IPictElement = {
|
||||||
|
readonly shape: XmlComponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPictElement = ({ shape }: IPictElement): XmlComponent =>
|
||||||
|
new BuilderElement<{ readonly style?: string }>({
|
||||||
|
name: "w:pict",
|
||||||
|
children: [shape],
|
||||||
|
});
|
58
src/file/textbox/shape/shape.spec.ts
Normal file
58
src/file/textbox/shape/shape.spec.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
import { Paragraph } from "@file/paragraph";
|
||||||
|
|
||||||
|
import { createShape } from "./shape";
|
||||||
|
|
||||||
|
describe("createShape", () => {
|
||||||
|
it("should work", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
createShape({
|
||||||
|
id: "test-id",
|
||||||
|
style: {
|
||||||
|
width: "10pt",
|
||||||
|
},
|
||||||
|
children: [new Paragraph("test-content")],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).toStrictEqual({
|
||||||
|
"v:shape": [
|
||||||
|
{ _attr: { id: "test-id", type: "#_x0000_t202", style: "width:10pt" } },
|
||||||
|
{
|
||||||
|
"v:textbox": [
|
||||||
|
{ _attr: { insetmode: "auto", style: "mso-fit-shape-to-text:t;" } },
|
||||||
|
{
|
||||||
|
"w:txbxContent": [
|
||||||
|
{ "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test-content"] }] }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create default styles", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
createShape({
|
||||||
|
id: "test-id",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).toStrictEqual({
|
||||||
|
"v:shape": [
|
||||||
|
{ _attr: { id: "test-id", type: "#_x0000_t202" } },
|
||||||
|
{
|
||||||
|
"v:textbox": [
|
||||||
|
{ _attr: { insetmode: "auto", style: "mso-fit-shape-to-text:t;" } },
|
||||||
|
{
|
||||||
|
"w:txbxContent": {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
125
src/file/textbox/shape/shape.ts
Normal file
125
src/file/textbox/shape/shape.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// https://c-rex.net/samples/ooxml/e1/Part3/OOXML_P3_Primer_OfficeArt_topic_ID0ELU5O.html
|
||||||
|
// http://webapp.docx4java.org/OnlineDemo/ecma376/VML/shape.html
|
||||||
|
import { ParagraphChild } from "@file/paragraph";
|
||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
import { LengthUnit } from "../types";
|
||||||
|
import { createVmlTextbox } from "../vml-textbox/vml-texbox";
|
||||||
|
|
||||||
|
const SHAPE_TYPE = "#_x0000_t202";
|
||||||
|
|
||||||
|
const styleToKeyMap: Record<keyof VmlShapeStyle, string> = {
|
||||||
|
flip: "flip",
|
||||||
|
height: "height",
|
||||||
|
left: "left",
|
||||||
|
marginBottom: "margin-bottom",
|
||||||
|
marginLeft: "margin-left",
|
||||||
|
marginRight: "margin-right",
|
||||||
|
marginTop: "margin-top",
|
||||||
|
positionHorizontal: "mso-position-horizontal",
|
||||||
|
positionHorizontalRelative: "mso-position-horizontal-relative",
|
||||||
|
positionVertical: "mso-position-vertical",
|
||||||
|
positionVerticalRelative: "mso-position-vertical-relative",
|
||||||
|
wrapDistanceBottom: "mso-wrap-distance-bottom",
|
||||||
|
wrapDistanceLeft: "mso-wrap-distance-left",
|
||||||
|
wrapDistanceRight: "mso-wrap-distance-right",
|
||||||
|
wrapDistanceTop: "mso-wrap-distance-top",
|
||||||
|
wrapEdited: "mso-wrap-edited",
|
||||||
|
wrapStyle: "mso-wrap-style",
|
||||||
|
position: "position",
|
||||||
|
rotation: "rotation",
|
||||||
|
top: "top",
|
||||||
|
visibility: "visibility",
|
||||||
|
width: "width",
|
||||||
|
zIndex: "z-index",
|
||||||
|
};
|
||||||
|
|
||||||
|
export type VmlShapeStyle = {
|
||||||
|
/** Specifies that the orientation of a shape is flipped. Default is no value. */
|
||||||
|
readonly flip?: "x" | "y" | "xy" | "yx";
|
||||||
|
/** Specifies the height of the containing block of the shape. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. */
|
||||||
|
readonly height?: LengthUnit;
|
||||||
|
/** Specifies the position of the left of the containing block of the shape relative to the element left of it in the flow of the page. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. This property shall not be used for shapes anchored inline. */
|
||||||
|
readonly left?: LengthUnit;
|
||||||
|
/** Specifies the position of the bottom of the containing block of the shape relative to the shape anchor. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. */
|
||||||
|
readonly marginBottom?: LengthUnit;
|
||||||
|
/** Specifies the position of the left of the containing block of the shape relative to the shape anchor. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. */
|
||||||
|
readonly marginLeft?: LengthUnit;
|
||||||
|
/** Specifies the position of the right of the containing block of the shape relative to the shape anchor. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. */
|
||||||
|
readonly marginRight?: LengthUnit;
|
||||||
|
/** Specifies the position of the top of the containing block of the shape relative to the shape anchor. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. */
|
||||||
|
readonly marginTop?: LengthUnit;
|
||||||
|
/** Specifies the horizontal positioning data for objects in WordprocessingML documents. Default is absolute. */
|
||||||
|
readonly positionHorizontal?: "absolute" | "left" | "center" | "right" | "inside" | "outside";
|
||||||
|
/** Specifies relative horizontal position data for objects in WordprocessingML documents. This modifies the mso-position-horizontal property. Default is text. */
|
||||||
|
readonly positionHorizontalRelative?: "margin" | "page" | "text" | "char";
|
||||||
|
/** Specifies the vertical positioning data for objects in WordprocessingML documents. Default is absolute. */
|
||||||
|
readonly positionVertical?: "absolute" | "left" | "center" | "right" | "inside" | "outside";
|
||||||
|
/** Specifies relative vertical position data for objects in WordprocessingML documents. This modifies the mso-position-vertical property. Default is text. */
|
||||||
|
readonly positionVerticalRelative?: "margin" | "page" | "text" | "char";
|
||||||
|
/** Specifies the distance from the bottom of the shape to the text that wraps around it. Default is 0 pt. Note that this property is different from the CSS margin property, which changes the origin of the shape to include the margin areas. This property does not change the origin. */
|
||||||
|
readonly wrapDistanceBottom?: number;
|
||||||
|
/** Specifies the distance from the left side of the shape to the text that wraps around it. Default is 0 pt. Note that this property is different from the CSS margin property, which changes the origin of the shape to include the margin areas. This property does not change the origin. */
|
||||||
|
readonly wrapDistanceLeft?: number;
|
||||||
|
/** Specifies the distance from the right side of the shape to the text that wraps around it. Default is 0 pt. Note that this property is different from the CSS margin property, which changes the origin of the shape to include the margin areas. This property does not change the origin. */
|
||||||
|
readonly wrapDistanceRight?: number;
|
||||||
|
/** Specifies the distance from the top of the shape to the text that wraps around it. Default is 0 pt. Note that this property is different from the CSS margin property, which changes the origin of the shape to include the margin areas. This property does not change the origin. */
|
||||||
|
readonly wrapDistanceTop?: number;
|
||||||
|
/** Specifies whether the wrap coordinates were customized by the user. If the wrap coordinates are generated by an editor, this property is true; otherwise they were customized by a user. Default is false. */
|
||||||
|
readonly wrapEdited?: boolean;
|
||||||
|
/** Specifies the wrapping mode for text in shapes in WordprocessingML documents. Default is square. */
|
||||||
|
readonly wrapStyle?: "square" | "none";
|
||||||
|
/** Specifies the type of positioning used to place an element. Default is static. When the element is contained inside a group, this property must be absolute. */
|
||||||
|
readonly position?: "static" | "absolute" | "relative";
|
||||||
|
/** Specifies the angle that a shape is rotated, in degrees. Default is 0. Positive angles are clockwise. */
|
||||||
|
readonly rotation?: number;
|
||||||
|
/** Specifies the position of the top of the containing block of the shape relative to the element above it in the flow of the page. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. This property shall not be used for shapes anchored inline. */
|
||||||
|
readonly top?: LengthUnit;
|
||||||
|
/** Specifies whether a shape is displayed. Only inherit and hidden are used; any other values are mapped to inherit. Default is inherit. */
|
||||||
|
readonly visibility?: "hidden" | "inherit";
|
||||||
|
/** Specifies the width of the containing block of the shape. Default is 0. It is specified in CSS units or, for elements in a group, in the coordinate system of the parent element. */
|
||||||
|
readonly width: LengthUnit;
|
||||||
|
/** Specifies the display order of overlapping shapes. Default is 0. This property shall not be used for shapes anchored inline. */
|
||||||
|
readonly zIndex?: "auto" | number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatShapeStyle = (style?: VmlShapeStyle): string | undefined =>
|
||||||
|
style
|
||||||
|
? Object.entries(style)
|
||||||
|
.map(([key, value]) => `${styleToKeyMap[key as keyof VmlShapeStyle]}:${value}`)
|
||||||
|
.join(";")
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
export const createShape = ({
|
||||||
|
id,
|
||||||
|
children,
|
||||||
|
type = SHAPE_TYPE,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly children?: readonly ParagraphChild[];
|
||||||
|
readonly type?: string;
|
||||||
|
readonly style?: VmlShapeStyle;
|
||||||
|
}): XmlComponent =>
|
||||||
|
new BuilderElement<{
|
||||||
|
readonly id: string;
|
||||||
|
readonly type?: string;
|
||||||
|
readonly style?: string;
|
||||||
|
}>({
|
||||||
|
name: "v:shape",
|
||||||
|
attributes: {
|
||||||
|
id: {
|
||||||
|
key: "id",
|
||||||
|
value: id,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
key: "type",
|
||||||
|
value: type,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
key: "style",
|
||||||
|
value: formatShapeStyle(style),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [createVmlTextbox({ style: "mso-fit-shape-to-text:t;", children })],
|
||||||
|
});
|
8
src/file/textbox/texbox-content/textbox-content.ts
Normal file
8
src/file/textbox/texbox-content/textbox-content.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { ParagraphChild } from "@file/paragraph";
|
||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
export const createTextboxContent = ({ children = [] }: { readonly children?: readonly ParagraphChild[] }): XmlComponent =>
|
||||||
|
new BuilderElement<{ readonly style?: string }>({
|
||||||
|
name: "w:txbxContent",
|
||||||
|
children: children as readonly XmlComponent[],
|
||||||
|
});
|
47
src/file/textbox/textbox.spec.ts
Normal file
47
src/file/textbox/textbox.spec.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
import { Paragraph } from "@file/paragraph";
|
||||||
|
|
||||||
|
import { Textbox } from "./textbox";
|
||||||
|
|
||||||
|
describe("VmlTextbox", () => {
|
||||||
|
it("should work", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
new Textbox({
|
||||||
|
style: {
|
||||||
|
width: "10pt",
|
||||||
|
},
|
||||||
|
children: [new Paragraph("test-content")],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).toStrictEqual({
|
||||||
|
"w:p": [
|
||||||
|
{
|
||||||
|
"w:pict": [
|
||||||
|
{
|
||||||
|
"v:shape": [
|
||||||
|
{ _attr: { id: expect.any(String), type: "#_x0000_t202", style: "width:10pt" } },
|
||||||
|
{
|
||||||
|
"v:textbox": [
|
||||||
|
{ _attr: { insetmode: "auto", style: "mso-fit-shape-to-text:t;" } },
|
||||||
|
{
|
||||||
|
"w:txbxContent": [
|
||||||
|
{
|
||||||
|
"w:p": [
|
||||||
|
{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test-content"] }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
27
src/file/textbox/textbox.ts
Normal file
27
src/file/textbox/textbox.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { FileChild } from "@file/file-child";
|
||||||
|
import { IParagraphOptions, ParagraphProperties } from "@file/paragraph";
|
||||||
|
import { uniqueId } from "@util/convenience-functions";
|
||||||
|
|
||||||
|
import { createPictElement } from "./pict-element/pict-element";
|
||||||
|
import { VmlShapeStyle, createShape } from "./shape/shape";
|
||||||
|
|
||||||
|
type ITextboxOptions = Omit<IParagraphOptions, "style"> & {
|
||||||
|
readonly style?: VmlShapeStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Textbox extends FileChild {
|
||||||
|
public constructor({ style, children, ...rest }: ITextboxOptions) {
|
||||||
|
super("w:p");
|
||||||
|
this.root.push(new ParagraphProperties(rest));
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
createPictElement({
|
||||||
|
shape: createShape({
|
||||||
|
children: children,
|
||||||
|
id: uniqueId(),
|
||||||
|
style: style,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
3
src/file/textbox/types.ts
Normal file
3
src/file/textbox/types.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { Percentage, RelativeMeasure, UniversalMeasure } from "@util/values";
|
||||||
|
|
||||||
|
export type LengthUnit = "auto" | number | Percentage | UniversalMeasure | RelativeMeasure;
|
42
src/file/textbox/vml-textbox/vml-texbox.ts
Normal file
42
src/file/textbox/vml-textbox/vml-texbox.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// http://webapp.docx4java.org/OnlineDemo/ecma376/VML/textbox.html
|
||||||
|
import { ParagraphChild } from "@file/paragraph";
|
||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
import { InsetMode } from "@util/types";
|
||||||
|
|
||||||
|
import { createTextboxContent } from "../texbox-content/textbox-content";
|
||||||
|
import { LengthUnit } from "../types";
|
||||||
|
|
||||||
|
// type VMLTextboxStyle = {
|
||||||
|
// readonly fontWeight?: "normal" | "lighter" | 100 | 200 | 300 | 400 | "bold" | "bolder" | 500 | 600 | 700 | 800 | 900;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export type IVTextboxOptions = {
|
||||||
|
readonly style?: string;
|
||||||
|
readonly children?: readonly ParagraphChild[];
|
||||||
|
readonly inset?: {
|
||||||
|
readonly top: LengthUnit;
|
||||||
|
readonly left: LengthUnit;
|
||||||
|
readonly bottom: LengthUnit;
|
||||||
|
readonly right: LengthUnit;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createVmlTextbox = ({ style, children, inset }: IVTextboxOptions): XmlComponent =>
|
||||||
|
new BuilderElement<{ readonly style?: string; readonly inset?: string; readonly insetMode?: InsetMode }>({
|
||||||
|
name: "v:textbox",
|
||||||
|
attributes: {
|
||||||
|
style: {
|
||||||
|
key: "style",
|
||||||
|
value: style,
|
||||||
|
},
|
||||||
|
insetMode: {
|
||||||
|
key: "insetmode",
|
||||||
|
value: inset ? "custom" : "auto",
|
||||||
|
},
|
||||||
|
inset: {
|
||||||
|
key: "inset",
|
||||||
|
value: inset ? `${inset.left}, ${inset.top}, ${inset.right}, ${inset.bottom}` : undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [createTextboxContent({ children })],
|
||||||
|
});
|
46
src/file/textbox/vml-textbox/vml-textbox.spec.ts
Normal file
46
src/file/textbox/vml-textbox/vml-textbox.spec.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
import { Paragraph } from "@file/paragraph";
|
||||||
|
|
||||||
|
import { createVmlTextbox } from "./vml-texbox";
|
||||||
|
|
||||||
|
describe("VmlTextbox", () => {
|
||||||
|
it("should work", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
createVmlTextbox({
|
||||||
|
style: "test-style",
|
||||||
|
children: [new Paragraph("test-content")],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).toStrictEqual({
|
||||||
|
"v:textbox": [
|
||||||
|
{ _attr: { insetmode: "auto", style: "test-style" } },
|
||||||
|
{ "w:txbxContent": [{ "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test-content"] }] }] }] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with inset", () => {
|
||||||
|
const tree = new Formatter().format(
|
||||||
|
createVmlTextbox({
|
||||||
|
style: "test-style",
|
||||||
|
children: [new Paragraph("test-content")],
|
||||||
|
inset: {
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(tree).toStrictEqual({
|
||||||
|
"v:textbox": [
|
||||||
|
{ _attr: { insetmode: "custom", style: "test-style", inset: "0, 0, 0, 0" } },
|
||||||
|
{ "w:txbxContent": [{ "w:p": [{ "w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "test-content"] }] }] }] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
7
src/util/types.ts
Normal file
7
src/util/types.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// <xsd:simpleType name="ST_InsetMode">
|
||||||
|
// <xsd:restriction base="xsd:string">
|
||||||
|
// <xsd:enumeration value="auto"/>
|
||||||
|
// <xsd:enumeration value="custom"/>
|
||||||
|
// </xsd:restriction>
|
||||||
|
// </xsd:simpleType>
|
||||||
|
export type InsetMode = "auto" | "custom"; // VML only type
|
@ -21,6 +21,16 @@ export type PositiveUniversalMeasure = `${number}${"mm" | "cm" | "in" | "pt" | "
|
|||||||
// </xsd:simpleType>
|
// </xsd:simpleType>
|
||||||
export type Percentage = `${"-" | ""}${number}%`;
|
export type Percentage = `${"-" | ""}${number}%`;
|
||||||
|
|
||||||
|
// <xsd:simpleType name="ST_PositivePercentage">
|
||||||
|
// <xsd:restriction base="ST_Percentage">
|
||||||
|
// <xsd:pattern value="[0-9]+(\.[0-9]+)?%"/>
|
||||||
|
// </xsd:restriction>
|
||||||
|
// </xsd:simpleType>
|
||||||
|
export type PositivePercentage = `${number}%`;
|
||||||
|
|
||||||
|
// Only applies to VmlTextbox so far
|
||||||
|
export type RelativeMeasure = `${"-" | ""}${number}${"em" | "ex"}`;
|
||||||
|
|
||||||
// <xsd:simpleType name="ST_DecimalNumber">
|
// <xsd:simpleType name="ST_DecimalNumber">
|
||||||
// <xsd:restriction base="xsd:integer"/>
|
// <xsd:restriction base="xsd:integer"/>
|
||||||
// </xsd:simpleType>
|
// </xsd:simpleType>
|
||||||
|
@ -76,6 +76,7 @@ export default defineConfig({
|
|||||||
'**/docs/**',
|
'**/docs/**',
|
||||||
'**/scripts/**',
|
'**/scripts/**',
|
||||||
'**/src/**/index.ts',
|
'**/src/**/index.ts',
|
||||||
|
'**/src/**/types.ts',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
|
Reference in New Issue
Block a user