@blocknote/xl-docx-exporter 0.33.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blocknote-xl-docx-exporter.cjs +2 -2
- package/dist/blocknote-xl-docx-exporter.cjs.map +1 -1
- package/dist/blocknote-xl-docx-exporter.js +129 -100
- package/dist/blocknote-xl-docx-exporter.js.map +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +7 -6
- package/src/docx/__snapshots__/basic/document.xml +2 -2
- package/src/docx/__snapshots__/withCustomOptions/document.xml.rels +0 -1
- package/src/docx/__snapshots__/withMultiColumn/document.xml +113 -0
- package/src/docx/__snapshots__/withMultiColumn/styles.xml +960 -0
- package/src/docx/defaultSchema/blocks.ts +55 -1
- package/src/docx/docxExporter.test.ts +86 -0
- package/src/docx/docxExporter.ts +27 -23
- package/types/src/docx/defaultSchema/blocks.d.ts +2 -1
- package/types/src/docx/defaultSchema/index.d.ts +33 -0
|
@@ -18,9 +18,12 @@ import {
|
|
|
18
18
|
Paragraph,
|
|
19
19
|
ParagraphChild,
|
|
20
20
|
ShadingType,
|
|
21
|
+
TableCell,
|
|
22
|
+
TableRow,
|
|
21
23
|
TextRun,
|
|
22
24
|
} from "docx";
|
|
23
25
|
import { Table } from "../util/Table.js";
|
|
26
|
+
import { multiColumnSchema } from "@blocknote/xl-multi-column";
|
|
24
27
|
|
|
25
28
|
function blockPropsToStyles(
|
|
26
29
|
props: Partial<DefaultProps>,
|
|
@@ -58,7 +61,9 @@ function blockPropsToStyles(
|
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
export const docxBlockMappingForDefaultSchema: BlockMapping<
|
|
61
|
-
DefaultBlockSchema &
|
|
64
|
+
DefaultBlockSchema &
|
|
65
|
+
typeof pageBreakSchema.blockSchema &
|
|
66
|
+
typeof multiColumnSchema.blockSchema,
|
|
62
67
|
any,
|
|
63
68
|
any,
|
|
64
69
|
| Promise<Paragraph[] | Paragraph | DocxTable>
|
|
@@ -187,6 +192,55 @@ export const docxBlockMappingForDefaultSchema: BlockMapping<
|
|
|
187
192
|
children: [new PageBreak()],
|
|
188
193
|
});
|
|
189
194
|
},
|
|
195
|
+
column: (block, _exporter, _nestingLevel, _numberedListIndex, children) => {
|
|
196
|
+
return new TableCell({
|
|
197
|
+
width: {
|
|
198
|
+
size: `${block.props.width * 100}%`,
|
|
199
|
+
type: "pct",
|
|
200
|
+
},
|
|
201
|
+
children: (children || []).flatMap((child) => {
|
|
202
|
+
if (Array.isArray(child)) {
|
|
203
|
+
return child;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return [child];
|
|
207
|
+
}),
|
|
208
|
+
}) as any;
|
|
209
|
+
},
|
|
210
|
+
columnList: (
|
|
211
|
+
_block,
|
|
212
|
+
_exporter,
|
|
213
|
+
_nestingLevel,
|
|
214
|
+
_numberedListIndex,
|
|
215
|
+
children,
|
|
216
|
+
) => {
|
|
217
|
+
return new DocxTable({
|
|
218
|
+
layout: "autofit",
|
|
219
|
+
borders: {
|
|
220
|
+
bottom: { style: "nil" },
|
|
221
|
+
top: { style: "nil" },
|
|
222
|
+
left: { style: "nil" },
|
|
223
|
+
right: { style: "nil" },
|
|
224
|
+
insideHorizontal: { style: "nil" },
|
|
225
|
+
insideVertical: { style: "nil" },
|
|
226
|
+
},
|
|
227
|
+
rows: [
|
|
228
|
+
new TableRow({
|
|
229
|
+
children: (children as unknown as TableCell[]).map(
|
|
230
|
+
(cell, _index, children) => {
|
|
231
|
+
return new TableCell({
|
|
232
|
+
width: {
|
|
233
|
+
size: `${(parseFloat(`${cell.options.width?.size || "100%"}`) / (children.length * 100)) * 100}%`,
|
|
234
|
+
type: "pct",
|
|
235
|
+
},
|
|
236
|
+
children: cell.options.children,
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
),
|
|
240
|
+
}),
|
|
241
|
+
],
|
|
242
|
+
});
|
|
243
|
+
},
|
|
190
244
|
image: async (block, exporter) => {
|
|
191
245
|
const blob = await exporter.resolveFile(block.props.url);
|
|
192
246
|
const { width, height } = await getImageDimensions(blob);
|
|
@@ -6,6 +6,8 @@ import { describe, expect, it } from "vitest";
|
|
|
6
6
|
import xmlFormat from "xml-formatter";
|
|
7
7
|
import { docxDefaultSchemaMappings } from "./defaultSchema/index.js";
|
|
8
8
|
import { DOCXExporter } from "./docxExporter.js";
|
|
9
|
+
import { ColumnBlock, ColumnListBlock } from "@blocknote/xl-multi-column";
|
|
10
|
+
import { partialBlocksToBlocksForTesting } from "@shared/formatConversionTestUtil.js";
|
|
9
11
|
|
|
10
12
|
const getZIPEntryContent = (entries: Entry[], fileName: string) => {
|
|
11
13
|
const entry = entries.find((entry) => {
|
|
@@ -109,6 +111,90 @@ describe("exporter", () => {
|
|
|
109
111
|
).toMatchFileSnapshot("__snapshots__/withCustomOptions/core.xml");
|
|
110
112
|
},
|
|
111
113
|
);
|
|
114
|
+
|
|
115
|
+
it(
|
|
116
|
+
"should export a document with a multi-column block",
|
|
117
|
+
{ timeout: 10000 },
|
|
118
|
+
async () => {
|
|
119
|
+
const schema = BlockNoteSchema.create({
|
|
120
|
+
blockSpecs: {
|
|
121
|
+
...defaultBlockSpecs,
|
|
122
|
+
pageBreak: PageBreak,
|
|
123
|
+
column: ColumnBlock,
|
|
124
|
+
columnList: ColumnListBlock,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const exporter = new DOCXExporter(schema, docxDefaultSchemaMappings);
|
|
128
|
+
const doc = await exporter.toDocxJsDocument(
|
|
129
|
+
partialBlocksToBlocksForTesting(schema, [
|
|
130
|
+
{
|
|
131
|
+
type: "columnList",
|
|
132
|
+
children: [
|
|
133
|
+
{
|
|
134
|
+
type: "column",
|
|
135
|
+
props: {
|
|
136
|
+
width: 0.8,
|
|
137
|
+
},
|
|
138
|
+
children: [
|
|
139
|
+
{
|
|
140
|
+
type: "paragraph",
|
|
141
|
+
content: "This paragraph is in a column!",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: "column",
|
|
147
|
+
props: {
|
|
148
|
+
width: 1.4,
|
|
149
|
+
},
|
|
150
|
+
children: [
|
|
151
|
+
{
|
|
152
|
+
type: "heading",
|
|
153
|
+
content: "So is this heading!",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: "column",
|
|
159
|
+
props: {
|
|
160
|
+
width: 0.8,
|
|
161
|
+
},
|
|
162
|
+
children: [
|
|
163
|
+
{
|
|
164
|
+
type: "paragraph",
|
|
165
|
+
content: "You can have multiple blocks in a column too",
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: "bulletListItem",
|
|
169
|
+
content: "Block 1",
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: "bulletListItem",
|
|
173
|
+
content: "Block 2",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: "bulletListItem",
|
|
177
|
+
content: "Block 3",
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
]),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
const blob = await Packer.toBlob(doc);
|
|
187
|
+
const zip = new ZipReader(new BlobReader(blob));
|
|
188
|
+
const entries = await zip.getEntries();
|
|
189
|
+
|
|
190
|
+
await expect(
|
|
191
|
+
prettify(await getZIPEntryContent(entries, "word/document.xml")),
|
|
192
|
+
).toMatchFileSnapshot("__snapshots__/withMultiColumn/document.xml");
|
|
193
|
+
await expect(
|
|
194
|
+
prettify(await getZIPEntryContent(entries, "word/styles.xml")),
|
|
195
|
+
).toMatchFileSnapshot("__snapshots__/withMultiColumn/styles.xml");
|
|
196
|
+
},
|
|
197
|
+
);
|
|
112
198
|
});
|
|
113
199
|
|
|
114
200
|
function prettify(sourceXml: string) {
|
package/src/docx/docxExporter.ts
CHANGED
|
@@ -114,22 +114,33 @@ export class DOCXExporter<
|
|
|
114
114
|
|
|
115
115
|
for (const b of blocks) {
|
|
116
116
|
let children = await this.transformBlocks(b.children, nestingLevel + 1);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
117
|
+
|
|
118
|
+
if (!["columnList", "column"].includes(b.type)) {
|
|
119
|
+
children = children.map((c, _i) => {
|
|
120
|
+
// NOTE: nested tables not supported (we can't insert the new Tab before a table)
|
|
121
|
+
if (
|
|
122
|
+
c instanceof Paragraph &&
|
|
123
|
+
!(c as any).properties.numberingReferences.length
|
|
124
|
+
) {
|
|
125
|
+
c.addRunToFront(
|
|
126
|
+
new TextRun({
|
|
127
|
+
children: [new Tab()],
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return c;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const self = await this.mapBlock(
|
|
136
|
+
b as any,
|
|
137
|
+
nestingLevel,
|
|
138
|
+
0 /*unused*/,
|
|
139
|
+
children,
|
|
140
|
+
); // TODO: any
|
|
141
|
+
if (["columnList", "column"].includes(b.type)) {
|
|
142
|
+
ret.push(self as Table);
|
|
143
|
+
} else if (Array.isArray(self)) {
|
|
133
144
|
ret.push(...self, ...children);
|
|
134
145
|
} else {
|
|
135
146
|
ret.push(self, ...children);
|
|
@@ -281,13 +292,6 @@ export class DOCXExporter<
|
|
|
281
292
|
],
|
|
282
293
|
});
|
|
283
294
|
|
|
284
|
-
// fix https://github.com/dolanmiu/docx/pull/2800/files
|
|
285
|
-
doc.Document.Relationships.createRelationship(
|
|
286
|
-
doc.Document.Relationships.RelationshipCount + 1,
|
|
287
|
-
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable",
|
|
288
|
-
"fontTable.xml",
|
|
289
|
-
);
|
|
290
|
-
|
|
291
295
|
return doc;
|
|
292
296
|
}
|
|
293
297
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { BlockMapping, DefaultBlockSchema, pageBreakSchema } from "@blocknote/core";
|
|
2
2
|
import { Table as DocxTable, Paragraph, ParagraphChild } from "docx";
|
|
3
|
-
|
|
3
|
+
import { multiColumnSchema } from "@blocknote/xl-multi-column";
|
|
4
|
+
export declare const docxBlockMappingForDefaultSchema: BlockMapping<DefaultBlockSchema & typeof pageBreakSchema.blockSchema & typeof multiColumnSchema.blockSchema, any, any, Promise<Paragraph[] | Paragraph | DocxTable> | Paragraph[] | Paragraph | DocxTable, ParagraphChild>;
|
|
@@ -539,6 +539,39 @@ export declare const docxDefaultSchemaMappings: {
|
|
|
539
539
|
isSelectable: false;
|
|
540
540
|
}, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
|
|
541
541
|
};
|
|
542
|
+
}> & import("@blocknote/core").BlockSchemaFromSpecs<{
|
|
543
|
+
column: {
|
|
544
|
+
config: {
|
|
545
|
+
type: "column";
|
|
546
|
+
content: "none";
|
|
547
|
+
propSchema: {
|
|
548
|
+
width: {
|
|
549
|
+
default: number;
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
};
|
|
553
|
+
implementation: import("@blocknote/core").TiptapBlockImplementation<{
|
|
554
|
+
type: "column";
|
|
555
|
+
content: "none";
|
|
556
|
+
propSchema: {
|
|
557
|
+
width: {
|
|
558
|
+
default: number;
|
|
559
|
+
};
|
|
560
|
+
};
|
|
561
|
+
}, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
|
|
562
|
+
};
|
|
563
|
+
columnList: {
|
|
564
|
+
config: {
|
|
565
|
+
type: "columnList";
|
|
566
|
+
content: "none";
|
|
567
|
+
propSchema: {};
|
|
568
|
+
};
|
|
569
|
+
implementation: import("@blocknote/core").TiptapBlockImplementation<{
|
|
570
|
+
type: "columnList";
|
|
571
|
+
content: "none";
|
|
572
|
+
propSchema: {};
|
|
573
|
+
}, any, import("@blocknote/core").InlineContentSchema, import("@blocknote/core").StyleSchema>;
|
|
574
|
+
};
|
|
542
575
|
}>, any, any, import("docx").Table | import("docx").Paragraph | import("docx").Paragraph[] | Promise<import("docx").Table | import("docx").Paragraph | import("docx").Paragraph[]>, import("docx").ParagraphChild>;
|
|
543
576
|
inlineContentMapping: import("@blocknote/core").InlineContentMapping<import("@blocknote/core").InlineContentSchemaFromSpecs<{
|
|
544
577
|
text: {
|