@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.
@@ -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 & typeof pageBreakSchema.blockSchema,
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) {
@@ -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
- children = children.map((c, _i) => {
118
- // NOTE: nested tables not supported (we can't insert the new Tab before a table)
119
- if (
120
- c instanceof Paragraph &&
121
- !(c as any).properties.numberingReferences.length
122
- ) {
123
- c.addRunToFront(
124
- new TextRun({
125
- children: [new Tab()],
126
- }),
127
- );
128
- }
129
- return c;
130
- });
131
- const self = await this.mapBlock(b as any, nestingLevel, 0 /*unused*/); // TODO: any
132
- if (Array.isArray(self)) {
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
- export declare const docxBlockMappingForDefaultSchema: BlockMapping<DefaultBlockSchema & typeof pageBreakSchema.blockSchema, any, any, Promise<Paragraph[] | Paragraph | DocxTable> | Paragraph[] | Paragraph | DocxTable, ParagraphChild>;
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: {