diff --git a/examples/05-interoperability/05-converting-blocks-to-pdf/.bnexample.json b/examples/05-interoperability/05-converting-blocks-to-pdf/.bnexample.json
index a98734f2a6..6115e659a5 100644
--- a/examples/05-interoperability/05-converting-blocks-to-pdf/.bnexample.json
+++ b/examples/05-interoperability/05-converting-blocks-to-pdf/.bnexample.json
@@ -5,6 +5,7 @@
"tags": ["Interoperability"],
"dependencies": {
"@blocknote/xl-pdf-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest",
"@react-pdf/renderer": "^4.3.0"
},
"pro": true
diff --git a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx
index 386649d509..5c3da5e063 100644
--- a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx
+++ b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx
@@ -4,6 +4,7 @@ import {
filterSuggestionItems,
withPageBreak,
} from "@blocknote/core";
+import * as locales from "@blocknote/core/locales";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
@@ -17,6 +18,12 @@ import {
PDFExporter,
pdfDefaultSchemaMappings,
} from "@blocknote/xl-pdf-exporter";
+import {
+ getMultiColumnSlashMenuItems,
+ multiColumnDropCursor,
+ locales as multiColumnLocales,
+ withMultiColumn,
+} from "@blocknote/xl-multi-column";
import { PDFViewer } from "@react-pdf/renderer";
import { useEffect, useMemo, useState } from "react";
@@ -28,7 +35,12 @@ export default function App() {
// Creates a new editor instance with some initial content.
const editor = useCreateBlockNote({
- schema: withPageBreak(BlockNoteSchema.create()),
+ schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())),
+ dropCursor: multiColumnDropCursor,
+ dictionary: {
+ ...locales.en,
+ multi_column: multiColumnLocales.en,
+ },
tables: {
splitCells: true,
cellBackgroundColor: true,
@@ -313,9 +325,72 @@ export default function App() {
console.log("Hello World", message);
};`,
},
+ {
+ type: "columnList",
+ children: [
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "This paragraph is in a column!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 1.4,
+ },
+ children: [
+ {
+ type: "heading",
+ content: "So is this heading!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "You can have multiple blocks in a column too",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 1",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 2",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 3",
+ },
+ ],
+ },
+ ],
+ },
],
});
-
+ const getSlashMenuItems = useMemo(() => {
+ return async (query: string) =>
+ filterSuggestionItems(
+ combineByGroup(
+ getDefaultReactSlashMenuItems(editor),
+ getPageBreakReactSlashMenuItems(editor),
+ getMultiColumnSlashMenuItems(editor),
+ ),
+ query,
+ );
+ }, [editor]);
const onChange = async () => {
const exporter = new PDFExporter(editor.schema, pdfDefaultSchemaMappings);
// Converts the editor's contents from Block objects to HTML and store to state.
@@ -330,13 +405,6 @@ export default function App() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
- const slashMenuItems = useMemo(() => {
- return combineByGroup(
- getDefaultReactSlashMenuItems(editor),
- getPageBreakReactSlashMenuItems(editor),
- );
- }, [editor]);
-
// Renders the editor instance, and its contents as HTML below.
return (
@@ -344,9 +412,7 @@ export default function App() {
- filterSuggestionItems(slashMenuItems, query)
- }
+ getItems={getSlashMenuItems}
/>
diff --git a/examples/05-interoperability/05-converting-blocks-to-pdf/package.json b/examples/05-interoperability/05-converting-blocks-to-pdf/package.json
index a20a4011c4..7207a6045c 100644
--- a/examples/05-interoperability/05-converting-blocks-to-pdf/package.json
+++ b/examples/05-interoperability/05-converting-blocks-to-pdf/package.json
@@ -18,6 +18,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"@blocknote/xl-pdf-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest",
"@react-pdf/renderer": "^4.3.0"
},
"devDependencies": {
diff --git a/examples/05-interoperability/06-converting-blocks-to-docx/.bnexample.json b/examples/05-interoperability/06-converting-blocks-to-docx/.bnexample.json
index 61ea12521a..e823472a1b 100644
--- a/examples/05-interoperability/06-converting-blocks-to-docx/.bnexample.json
+++ b/examples/05-interoperability/06-converting-blocks-to-docx/.bnexample.json
@@ -5,6 +5,7 @@
"tags": [""],
"dependencies": {
"@blocknote/xl-docx-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest",
"docx": "^9.0.2"
},
"pro": true
diff --git a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx
index 8839243558..597f3793b7 100644
--- a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx
+++ b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx
@@ -4,6 +4,7 @@ import {
filterSuggestionItems,
withPageBreak,
} from "@blocknote/core";
+import * as locales from "@blocknote/core/locales";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
@@ -17,6 +18,12 @@ import {
DOCXExporter,
docxDefaultSchemaMappings,
} from "@blocknote/xl-docx-exporter";
+import {
+ getMultiColumnSlashMenuItems,
+ multiColumnDropCursor,
+ locales as multiColumnLocales,
+ withMultiColumn,
+} from "@blocknote/xl-multi-column";
import { useMemo } from "react";
import "./styles.css";
@@ -24,7 +31,12 @@ import "./styles.css";
export default function App() {
// Creates a new editor instance with some initial content.
const editor = useCreateBlockNote({
- schema: withPageBreak(BlockNoteSchema.create()),
+ schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())),
+ dropCursor: multiColumnDropCursor,
+ dictionary: {
+ ...locales.en,
+ multi_column: multiColumnLocales.en,
+ },
tables: {
splitCells: true,
cellBackgroundColor: true,
@@ -309,9 +321,73 @@ export default function App() {
console.log("Hello World", message);
};`,
},
+
+ {
+ type: "columnList",
+ children: [
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "This paragraph is in a column!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 1.4,
+ },
+ children: [
+ {
+ type: "heading",
+ content: "So is this heading!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "You can have multiple blocks in a column too",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 1",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 2",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 3",
+ },
+ ],
+ },
+ ],
+ },
],
});
-
+ const getSlashMenuItems = useMemo(() => {
+ return async (query: string) =>
+ filterSuggestionItems(
+ combineByGroup(
+ getDefaultReactSlashMenuItems(editor),
+ getPageBreakReactSlashMenuItems(editor),
+ getMultiColumnSlashMenuItems(editor),
+ ),
+ query,
+ );
+ }, [editor]);
const onDownloadClick = async () => {
const exporter = new DOCXExporter(editor.schema, docxDefaultSchemaMappings);
@@ -332,13 +408,6 @@ export default function App() {
window.URL.revokeObjectURL(link.href);
};
- const slashMenuItems = useMemo(() => {
- return combineByGroup(
- getDefaultReactSlashMenuItems(editor),
- getPageBreakReactSlashMenuItems(editor),
- );
- }, [editor]);
-
// Renders the editor instance, and its contents as HTML below.
return (
@@ -351,9 +420,7 @@ export default function App() {
- filterSuggestionItems(slashMenuItems, query)
- }
+ getItems={getSlashMenuItems}
/>
diff --git a/examples/05-interoperability/06-converting-blocks-to-docx/package.json b/examples/05-interoperability/06-converting-blocks-to-docx/package.json
index 092a4761c2..7dc4f636b0 100644
--- a/examples/05-interoperability/06-converting-blocks-to-docx/package.json
+++ b/examples/05-interoperability/06-converting-blocks-to-docx/package.json
@@ -18,6 +18,7 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"@blocknote/xl-docx-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest",
"docx": "^9.0.2"
},
"devDependencies": {
diff --git a/examples/05-interoperability/07-converting-blocks-to-odt/.bnexample.json b/examples/05-interoperability/07-converting-blocks-to-odt/.bnexample.json
index b17ea0f26b..7e3174aeea 100644
--- a/examples/05-interoperability/07-converting-blocks-to-odt/.bnexample.json
+++ b/examples/05-interoperability/07-converting-blocks-to-odt/.bnexample.json
@@ -4,7 +4,8 @@
"author": "areknawo",
"tags": [""],
"dependencies": {
- "@blocknote/xl-odt-exporter": "latest"
+ "@blocknote/xl-odt-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest"
},
"pro": true
}
diff --git a/examples/05-interoperability/07-converting-blocks-to-odt/App.tsx b/examples/05-interoperability/07-converting-blocks-to-odt/App.tsx
index cb99a040e1..b346e939a1 100644
--- a/examples/05-interoperability/07-converting-blocks-to-odt/App.tsx
+++ b/examples/05-interoperability/07-converting-blocks-to-odt/App.tsx
@@ -4,6 +4,7 @@ import {
filterSuggestionItems,
withPageBreak,
} from "@blocknote/core";
+import * as locales from "@blocknote/core/locales";
import "@blocknote/core/fonts/inter.css";
import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
@@ -17,6 +18,12 @@ import {
ODTExporter,
odtDefaultSchemaMappings,
} from "@blocknote/xl-odt-exporter";
+import {
+ getMultiColumnSlashMenuItems,
+ multiColumnDropCursor,
+ locales as multiColumnLocales,
+ withMultiColumn,
+} from "@blocknote/xl-multi-column";
import { useMemo } from "react";
import "./styles.css";
@@ -24,7 +31,12 @@ import "./styles.css";
export default function App() {
// Creates a new editor instance with some initial content.
const editor = useCreateBlockNote({
- schema: withPageBreak(BlockNoteSchema.create()),
+ schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())),
+ dropCursor: multiColumnDropCursor,
+ dictionary: {
+ ...locales.en,
+ multi_column: multiColumnLocales.en,
+ },
tables: {
splitCells: true,
cellTextColor: true,
@@ -308,9 +320,72 @@ export default function App() {
console.log("Hello World", message);
};`,
},
+ {
+ type: "columnList",
+ children: [
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "This paragraph is in a column!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 1.4,
+ },
+ children: [
+ {
+ type: "heading",
+ content: "So is this heading!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "You can have multiple blocks in a column too",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 1",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 2",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 3",
+ },
+ ],
+ },
+ ],
+ },
],
});
-
+ const getSlashMenuItems = useMemo(() => {
+ return async (query: string) =>
+ filterSuggestionItems(
+ combineByGroup(
+ getDefaultReactSlashMenuItems(editor),
+ getPageBreakReactSlashMenuItems(editor),
+ getMultiColumnSlashMenuItems(editor),
+ ),
+ query,
+ );
+ }, [editor]);
const onDownloadClick = async () => {
const exporter = new ODTExporter(editor.schema, odtDefaultSchemaMappings);
@@ -331,13 +406,6 @@ export default function App() {
window.URL.revokeObjectURL(link.href);
};
- const slashMenuItems = useMemo(() => {
- return combineByGroup(
- getDefaultReactSlashMenuItems(editor),
- getPageBreakReactSlashMenuItems(editor),
- );
- }, [editor]);
-
// Renders the editor instance, and its contents as HTML below.
return (
@@ -350,9 +418,7 @@ export default function App() {
- filterSuggestionItems(slashMenuItems, query)
- }
+ getItems={getSlashMenuItems}
/>
diff --git a/examples/05-interoperability/07-converting-blocks-to-odt/package.json b/examples/05-interoperability/07-converting-blocks-to-odt/package.json
index 4318d50d5b..d1092da393 100644
--- a/examples/05-interoperability/07-converting-blocks-to-odt/package.json
+++ b/examples/05-interoperability/07-converting-blocks-to-odt/package.json
@@ -17,7 +17,8 @@
"@blocknote/shadcn": "latest",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "@blocknote/xl-odt-exporter": "latest"
+ "@blocknote/xl-odt-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest"
},
"devDependencies": {
"@types/react": "^18.0.25",
diff --git a/packages/core/src/exporter/Exporter.ts b/packages/core/src/exporter/Exporter.ts
index 75cf9a1705..2b1aa65050 100644
--- a/packages/core/src/exporter/Exporter.ts
+++ b/packages/core/src/exporter/Exporter.ts
@@ -90,12 +90,14 @@ export abstract class Exporter<
block: BlockFromConfig,
nestingLevel: number,
numberedListIndex: number,
+ children?: Array>,
) {
return this.mappings.blockMapping[block.type](
block,
this,
nestingLevel,
numberedListIndex,
+ children,
);
}
}
diff --git a/packages/core/src/exporter/mapping.ts b/packages/core/src/exporter/mapping.ts
index 2700d279ea..6790747169 100644
--- a/packages/core/src/exporter/mapping.ts
+++ b/packages/core/src/exporter/mapping.ts
@@ -27,6 +27,7 @@ export type BlockMapping<
exporter: Exporter,
nestingLevel: number,
numberedListIndex?: number,
+ children?: Array>,
) => RB | Promise;
};
diff --git a/packages/xl-docx-exporter/package.json b/packages/xl-docx-exporter/package.json
index 5975fcec5d..7734081304 100644
--- a/packages/xl-docx-exporter/package.json
+++ b/packages/xl-docx-exporter/package.json
@@ -58,6 +58,7 @@
},
"dependencies": {
"@blocknote/core": "0.33.0",
+ "@blocknote/xl-multi-column": "0.33.0",
"buffer": "^6.0.3",
"docx": "^9.0.2",
"image-meta": "^0.2.1"
diff --git a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml
index 3e8d279cc8..06e915d628 100644
--- a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml
+++ b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml
@@ -418,7 +418,7 @@
-
+
@@ -471,7 +471,7 @@
-
+
diff --git a/packages/xl-docx-exporter/src/docx/__snapshots__/withCustomOptions/document.xml.rels b/packages/xl-docx-exporter/src/docx/__snapshots__/withCustomOptions/document.xml.rels
index df89e73844..04d8b5e497 100644
--- a/packages/xl-docx-exporter/src/docx/__snapshots__/withCustomOptions/document.xml.rels
+++ b/packages/xl-docx-exporter/src/docx/__snapshots__/withCustomOptions/document.xml.rels
@@ -7,7 +7,6 @@
-
diff --git a/packages/xl-docx-exporter/src/docx/__snapshots__/withMultiColumn/document.xml b/packages/xl-docx-exporter/src/docx/__snapshots__/withMultiColumn/document.xml
new file mode 100644
index 0000000000..4c3b378ced
--- /dev/null
+++ b/packages/xl-docx-exporter/src/docx/__snapshots__/withMultiColumn/document.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This paragraph is in a column!
+
+
+
+
+
+
+
+
+
+
+
+
+ So is this heading!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ You can have multiple blocks in a column too
+
+
+
+
+
+
+
+
+
+
+
+ Block 1
+
+
+
+
+
+
+
+
+
+
+
+ Block 2
+
+
+
+
+
+
+
+
+
+
+
+ Block 3
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/xl-docx-exporter/src/docx/__snapshots__/withMultiColumn/styles.xml b/packages/xl-docx-exporter/src/docx/__snapshots__/withMultiColumn/styles.xml
new file mode 100644
index 0000000000..d23b5bb2ff
--- /dev/null
+++ b/packages/xl-docx-exporter/src/docx/__snapshots__/withMultiColumn/styles.xml
@@ -0,0 +1,960 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts
index 7d5cce4c1c..f1f914a336 100644
--- a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts
+++ b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts
@@ -18,9 +18,12 @@ import {
Paragraph,
ParagraphChild,
ShadingType,
+ TableCell,
+ TableRow,
TextRun,
} from "docx";
import { Table } from "../util/Table.js";
+import { multiColumnSchema } from "@blocknote/xl-multi-column";
function blockPropsToStyles(
props: Partial,
@@ -58,7 +61,9 @@ function blockPropsToStyles(
};
}
export const docxBlockMappingForDefaultSchema: BlockMapping<
- DefaultBlockSchema & typeof pageBreakSchema.blockSchema,
+ DefaultBlockSchema &
+ typeof pageBreakSchema.blockSchema &
+ typeof multiColumnSchema.blockSchema,
any,
any,
| Promise
@@ -187,6 +192,55 @@ export const docxBlockMappingForDefaultSchema: BlockMapping<
children: [new PageBreak()],
});
},
+ column: (block, _exporter, _nestingLevel, _numberedListIndex, children) => {
+ return new TableCell({
+ width: {
+ size: `${block.props.width * 100}%`,
+ type: "pct",
+ },
+ children: (children || []).flatMap((child) => {
+ if (Array.isArray(child)) {
+ return child;
+ }
+
+ return [child];
+ }),
+ }) as any;
+ },
+ columnList: (
+ _block,
+ _exporter,
+ _nestingLevel,
+ _numberedListIndex,
+ children,
+ ) => {
+ return new DocxTable({
+ layout: "autofit",
+ borders: {
+ bottom: { style: "nil" },
+ top: { style: "nil" },
+ left: { style: "nil" },
+ right: { style: "nil" },
+ insideHorizontal: { style: "nil" },
+ insideVertical: { style: "nil" },
+ },
+ rows: [
+ new TableRow({
+ children: (children as unknown as TableCell[]).map(
+ (cell, _index, children) => {
+ return new TableCell({
+ width: {
+ size: `${(parseFloat(`${cell.options.width?.size || "100%"}`) / (children.length * 100)) * 100}%`,
+ type: "pct",
+ },
+ children: cell.options.children,
+ });
+ },
+ ),
+ }),
+ ],
+ });
+ },
image: async (block, exporter) => {
const blob = await exporter.resolveFile(block.props.url);
const { width, height } = await getImageDimensions(blob);
diff --git a/packages/xl-docx-exporter/src/docx/docxExporter.test.ts b/packages/xl-docx-exporter/src/docx/docxExporter.test.ts
index 813b826eb4..d9d3adeb2b 100644
--- a/packages/xl-docx-exporter/src/docx/docxExporter.test.ts
+++ b/packages/xl-docx-exporter/src/docx/docxExporter.test.ts
@@ -6,6 +6,8 @@ import { describe, expect, it } from "vitest";
import xmlFormat from "xml-formatter";
import { docxDefaultSchemaMappings } from "./defaultSchema/index.js";
import { DOCXExporter } from "./docxExporter.js";
+import { ColumnBlock, ColumnListBlock } from "@blocknote/xl-multi-column";
+import { partialBlocksToBlocksForTesting } from "@shared/formatConversionTestUtil.js";
const getZIPEntryContent = (entries: Entry[], fileName: string) => {
const entry = entries.find((entry) => {
@@ -109,6 +111,90 @@ describe("exporter", () => {
).toMatchFileSnapshot("__snapshots__/withCustomOptions/core.xml");
},
);
+
+ it(
+ "should export a document with a multi-column block",
+ { timeout: 10000 },
+ async () => {
+ const schema = BlockNoteSchema.create({
+ blockSpecs: {
+ ...defaultBlockSpecs,
+ pageBreak: PageBreak,
+ column: ColumnBlock,
+ columnList: ColumnListBlock,
+ },
+ });
+ const exporter = new DOCXExporter(schema, docxDefaultSchemaMappings);
+ const doc = await exporter.toDocxJsDocument(
+ partialBlocksToBlocksForTesting(schema, [
+ {
+ type: "columnList",
+ children: [
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "This paragraph is in a column!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 1.4,
+ },
+ children: [
+ {
+ type: "heading",
+ content: "So is this heading!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "You can have multiple blocks in a column too",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 1",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 2",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 3",
+ },
+ ],
+ },
+ ],
+ },
+ ]),
+ );
+
+ const blob = await Packer.toBlob(doc);
+ const zip = new ZipReader(new BlobReader(blob));
+ const entries = await zip.getEntries();
+
+ await expect(
+ prettify(await getZIPEntryContent(entries, "word/document.xml")),
+ ).toMatchFileSnapshot("__snapshots__/withMultiColumn/document.xml");
+ await expect(
+ prettify(await getZIPEntryContent(entries, "word/styles.xml")),
+ ).toMatchFileSnapshot("__snapshots__/withMultiColumn/styles.xml");
+ },
+ );
});
function prettify(sourceXml: string) {
diff --git a/packages/xl-docx-exporter/src/docx/docxExporter.ts b/packages/xl-docx-exporter/src/docx/docxExporter.ts
index a9f7f07d56..9e2ddfd6b6 100644
--- a/packages/xl-docx-exporter/src/docx/docxExporter.ts
+++ b/packages/xl-docx-exporter/src/docx/docxExporter.ts
@@ -114,22 +114,33 @@ export class DOCXExporter<
for (const b of blocks) {
let children = await this.transformBlocks(b.children, nestingLevel + 1);
- children = children.map((c, _i) => {
- // NOTE: nested tables not supported (we can't insert the new Tab before a table)
- if (
- c instanceof Paragraph &&
- !(c as any).properties.numberingReferences.length
- ) {
- c.addRunToFront(
- new TextRun({
- children: [new Tab()],
- }),
- );
- }
- return c;
- });
- const self = await this.mapBlock(b as any, nestingLevel, 0 /*unused*/); // TODO: any
- if (Array.isArray(self)) {
+
+ if (!["columnList", "column"].includes(b.type)) {
+ children = children.map((c, _i) => {
+ // NOTE: nested tables not supported (we can't insert the new Tab before a table)
+ if (
+ c instanceof Paragraph &&
+ !(c as any).properties.numberingReferences.length
+ ) {
+ c.addRunToFront(
+ new TextRun({
+ children: [new Tab()],
+ }),
+ );
+ }
+ return c;
+ });
+ }
+
+ const self = await this.mapBlock(
+ b as any,
+ nestingLevel,
+ 0 /*unused*/,
+ children,
+ ); // TODO: any
+ if (["columnList", "column"].includes(b.type)) {
+ ret.push(self as Table);
+ } else if (Array.isArray(self)) {
ret.push(...self, ...children);
} else {
ret.push(self, ...children);
@@ -281,13 +292,6 @@ export class DOCXExporter<
],
});
- // fix https://github.com/dolanmiu/docx/pull/2800/files
- doc.Document.Relationships.createRelationship(
- doc.Document.Relationships.RelationshipCount + 1,
- "http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable",
- "fontTable.xml",
- );
-
return doc;
}
}
diff --git a/packages/xl-odt-exporter/package.json b/packages/xl-odt-exporter/package.json
index 5a9b18f979..5f8ff78551 100644
--- a/packages/xl-odt-exporter/package.json
+++ b/packages/xl-odt-exporter/package.json
@@ -58,6 +58,7 @@
},
"dependencies": {
"@blocknote/core": "0.33.0",
+ "@blocknote/xl-multi-column": "0.33.0",
"@zip.js/zip.js": "^2.7.57",
"buffer": "^6.0.3",
"image-meta": "^0.2.1"
diff --git a/packages/xl-odt-exporter/src/odt/__snapshots__/withMultiColumn/content.xml b/packages/xl-odt-exporter/src/odt/__snapshots__/withMultiColumn/content.xml
new file mode 100644
index 0000000000..fd41c45fa7
--- /dev/null
+++ b/packages/xl-odt-exporter/src/odt/__snapshots__/withMultiColumn/content.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This paragraph is in a column!
+
+
+
+
+ So is this heading!
+
+
+
+
+ You can have multiple blocks in a column too
+
+
+
+
+ Block 1
+
+
+
+
+
+
+ Block 2
+
+
+
+
+
+
+ Block 3
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/xl-odt-exporter/src/odt/__snapshots__/withMultiColumn/styles.xml b/packages/xl-odt-exporter/src/odt/__snapshots__/withMultiColumn/styles.xml
new file mode 100644
index 0000000000..9a53dac929
--- /dev/null
+++ b/packages/xl-odt-exporter/src/odt/__snapshots__/withMultiColumn/styles.xml
@@ -0,0 +1,599 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/xl-odt-exporter/src/odt/defaultSchema/blocks.tsx b/packages/xl-odt-exporter/src/odt/defaultSchema/blocks.tsx
index fab2516523..0c2e512793 100644
--- a/packages/xl-odt-exporter/src/odt/defaultSchema/blocks.tsx
+++ b/packages/xl-odt-exporter/src/odt/defaultSchema/blocks.tsx
@@ -1,4 +1,5 @@
import {
+ BlockFromConfig,
BlockMapping,
DefaultBlockSchema,
DefaultProps,
@@ -8,6 +9,7 @@ import {
TableCell,
} from "@blocknote/core";
import { ODTExporter } from "../odtExporter.js";
+import { multiColumnSchema } from "@blocknote/xl-multi-column";
export const getTabs = (nestingLevel: number) => {
return Array.from({ length: nestingLevel }, () => );
@@ -168,7 +170,9 @@ const wrapWithLists = (
};
export const odtBlockMappingForDefaultSchema: BlockMapping<
- DefaultBlockSchema & typeof pageBreakSchema.blockSchema,
+ DefaultBlockSchema &
+ typeof pageBreakSchema.blockSchema &
+ typeof multiColumnSchema.blockSchema,
any,
any,
React.ReactNode,
@@ -309,6 +313,69 @@ export const odtBlockMappingForDefaultSchema: BlockMapping<
return ;
},
+ column: (_block, exporter, _nestingLevel, _numberedListIndex, children) => {
+ const ex = exporter as ODTExporter;
+ const style = ex.registerStyle((name) => (
+
+
+
+ ));
+
+ return (
+ {children}
+ );
+ },
+ columnList: (
+ block,
+ exporter,
+ _nestingLevel,
+ _numberedListIndex,
+ children,
+ ) => {
+ const blockWithChildren = block as BlockFromConfig<
+ {
+ type: "columnList";
+ content: "none";
+ propSchema: Record;
+ },
+ any,
+ any
+ >;
+ const ex = exporter as ODTExporter;
+ const style = ex.registerStyle((name) => (
+
+
+
+ ));
+
+ return (
+
+ {(blockWithChildren.children || []).map((column, index) => {
+ const style = ex.registerStyle((name) => (
+
+
+
+ ));
+
+ return ;
+ })}
+ {children}
+
+ );
+ },
+
image: async (block, exporter) => {
const odtExporter = exporter as ODTExporter;
diff --git a/packages/xl-odt-exporter/src/odt/odtExporter.test.ts b/packages/xl-odt-exporter/src/odt/odtExporter.test.ts
index d048a01a6b..c5e97e7718 100644
--- a/packages/xl-odt-exporter/src/odt/odtExporter.test.ts
+++ b/packages/xl-odt-exporter/src/odt/odtExporter.test.ts
@@ -5,6 +5,8 @@ import { beforeAll, describe, expect, it } from "vitest";
import xmlFormat from "xml-formatter";
import { odtDefaultSchemaMappings } from "./defaultSchema/index.js";
import { ODTExporter } from "./odtExporter.js";
+import { ColumnBlock, ColumnListBlock } from "@blocknote/xl-multi-column";
+import { partialBlocksToBlocksForTesting } from "@shared/formatConversionTestUtil.js";
beforeAll(async () => {
// @ts-ignore
@@ -51,6 +53,84 @@ describe("exporter", () => {
});
},
);
+
+ it(
+ "should export a document with a multi-column block",
+ { timeout: 10000 },
+ async () => {
+ const schema = BlockNoteSchema.create({
+ blockSpecs: {
+ ...defaultBlockSpecs,
+ pageBreak: PageBreak,
+ column: ColumnBlock,
+ columnList: ColumnListBlock,
+ },
+ });
+ const exporter = new ODTExporter(schema, odtDefaultSchemaMappings);
+ const odt = await exporter.toODTDocument(
+ partialBlocksToBlocksForTesting(schema, [
+ {
+ type: "columnList",
+ children: [
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "This paragraph is in a column!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 1.4,
+ },
+ children: [
+ {
+ type: "heading",
+ content: "So is this heading!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "You can have multiple blocks in a column too",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 1",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 2",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 3",
+ },
+ ],
+ },
+ ],
+ },
+ ]),
+ );
+
+ await testODTDocumentAgainstSnapshot(odt, {
+ styles: "__snapshots__/withMultiColumn/styles.xml",
+ content: "__snapshots__/withMultiColumn/content.xml",
+ });
+ },
+ );
});
async function testODTDocumentAgainstSnapshot(
diff --git a/packages/xl-odt-exporter/src/odt/odtExporter.tsx b/packages/xl-odt-exporter/src/odt/odtExporter.tsx
index eb3cf08369..ba52c22f25 100644
--- a/packages/xl-odt-exporter/src/odt/odtExporter.tsx
+++ b/packages/xl-odt-exporter/src/odt/odtExporter.tsx
@@ -126,20 +126,32 @@ export class ODTExporter<
numberedListIndex = 0;
}
- const children = await this.transformBlocks(
- block.children,
- nestingLevel + 1,
- );
-
- const content = await this.mapBlock(
- block as any,
- nestingLevel,
- numberedListIndex,
- );
-
- ret.push(content);
- if (children.length > 0) {
- ret.push(...children);
+ if (["columnList", "column"].includes(block.type)) {
+ const children = await this.transformBlocks(block.children, 0);
+ const content = await this.mapBlock(
+ block as any,
+ 0,
+ numberedListIndex,
+ children,
+ );
+
+ ret.push(content);
+ } else {
+ const children = await this.transformBlocks(
+ block.children,
+ nestingLevel + 1,
+ );
+ const content = await this.mapBlock(
+ block as any,
+ nestingLevel,
+ numberedListIndex,
+ children,
+ );
+
+ ret.push(content);
+ if (children.length > 0) {
+ ret.push(...children);
+ }
}
}
diff --git a/packages/xl-pdf-exporter/package.json b/packages/xl-pdf-exporter/package.json
index a1f43732a0..1412b0e600 100644
--- a/packages/xl-pdf-exporter/package.json
+++ b/packages/xl-pdf-exporter/package.json
@@ -58,6 +58,7 @@
"dependencies": {
"@blocknote/core": "0.33.0",
"@blocknote/react": "0.33.0",
+ "@blocknote/xl-multi-column": "0.33.0",
"@react-pdf/renderer": "^4.3.0",
"buffer": "^6.0.3",
"docx": "^9.0.2"
diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx
index 14c309af12..a9b3f7ef67 100644
--- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx
+++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx
@@ -137,7 +137,8 @@
@@ -159,7 +160,8 @@
diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx
index cad7caa99b..89d1d756ea 100644
--- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx
+++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx
@@ -145,7 +145,8 @@
@@ -167,7 +168,8 @@
diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithMultiColumn.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithMultiColumn.jsx
new file mode 100644
index 0000000000..9a535d0d5f
--- /dev/null
+++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithMultiColumn.jsx
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+ This paragraph is in a column!
+
+
+
+
+
+
+
+
+
+
+ So is this heading!
+
+
+
+
+
+
+
+
+
+
+ You can have multiple blocks in a column too
+
+
+
+
+
+
+
+
+
+ Block 1
+
+
+
+
+
+
+
+
+
+
+ Block 2
+
+
+
+
+
+
+
+
+
+
+ Block 3
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx b/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx
index 4ea71c0d53..693f77783b 100644
--- a/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx
+++ b/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx
@@ -5,6 +5,7 @@ import {
pageBreakSchema,
StyledText,
} from "@blocknote/core";
+import { multiColumnSchema } from "@blocknote/xl-multi-column";
import { Image, Link, Path, Svg, Text, View } from "@react-pdf/renderer";
import {
BULLET_MARKER,
@@ -19,7 +20,9 @@ const PIXELS_PER_POINT = 0.75;
const FONT_SIZE = 16;
export const pdfBlockMappingForDefaultSchema: BlockMapping<
- DefaultBlockSchema & typeof pageBreakSchema.blockSchema,
+ DefaultBlockSchema &
+ typeof pageBreakSchema.blockSchema &
+ typeof multiColumnSchema.blockSchema,
any,
any,
React.ReactElement,
@@ -88,6 +91,7 @@ export const pdfBlockMappingForDefaultSchema: BlockMapping<
key={"heading" + block.id}
style={{
fontSize: levelFontSizeEM * FONT_SIZE * PIXELS_PER_POINT,
+ lineHeight: 1.25,
fontWeight: 700,
}}
>
@@ -146,6 +150,28 @@ export const pdfBlockMappingForDefaultSchema: BlockMapping<
pageBreak: () => {
return ;
},
+ column: (block, _exporter, _nestingLevel, _numberedListIndex, children) => {
+ return {children};
+ },
+ columnList: (
+ _block,
+ _exporter,
+ _nestingLevel,
+ _numberedListIndex,
+ children,
+ ) => {
+ return (
+
+ {children}
+
+ );
+ },
audio: (block, exporter) => {
return (
diff --git a/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx b/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx
index 51095f90ac..584961f750 100644
--- a/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx
+++ b/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx
@@ -8,12 +8,14 @@ import {
defaultStyleSpecs,
PageBreak,
} from "@blocknote/core";
+import { ColumnBlock, ColumnListBlock } from "@blocknote/xl-multi-column";
import { Text } from "@react-pdf/renderer";
import { testDocument } from "@shared/testDocument.js";
import reactElementToJSXString from "react-element-to-jsx-string";
import { describe, expect, it } from "vitest";
import { pdfDefaultSchemaMappings } from "./defaultSchema/index.js";
import { PDFExporter } from "./pdfExporter.js";
+import { partialBlocksToBlocksForTesting } from "@shared/formatConversionTestUtil.js";
// import * as ReactPDF from "@react-pdf/renderer";
// expect.extend({ toMatchImageSnapshot });
// import { toMatchImageSnapshot } from "jest-image-snapshot";
@@ -28,6 +30,8 @@ describe("exporter", () => {
blockSpecs: {
...defaultBlockSpecs,
pageBreak: PageBreak,
+ column: ColumnBlock,
+ columnList: ColumnListBlock,
extraBlock: createBlockSpec(
{
content: "none",
@@ -158,7 +162,12 @@ describe("exporter", () => {
it("should export a document", async () => {
const exporter = new PDFExporter(
BlockNoteSchema.create({
- blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak },
+ blockSpecs: {
+ ...defaultBlockSpecs,
+ pageBreak: PageBreak,
+ column: ColumnBlock,
+ columnList: ColumnListBlock,
+ },
}),
pdfDefaultSchemaMappings,
);
@@ -191,7 +200,12 @@ describe("exporter", () => {
it("should export a document with header and footer", async () => {
const exporter = new PDFExporter(
BlockNoteSchema.create({
- blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak },
+ blockSpecs: {
+ ...defaultBlockSpecs,
+ pageBreak: PageBreak,
+ column: ColumnBlock,
+ columnList: ColumnListBlock,
+ },
}),
pdfDefaultSchemaMappings,
);
@@ -210,4 +224,77 @@ describe("exporter", () => {
// `${__dirname}/exampleWithHeaderAndFooter.pdf`
// );
});
+ it("should export a document with a multi-column block", async () => {
+ const schema = BlockNoteSchema.create({
+ blockSpecs: {
+ ...defaultBlockSpecs,
+ pageBreak: PageBreak,
+ column: ColumnBlock,
+ columnList: ColumnListBlock,
+ },
+ });
+ const exporter = new PDFExporter(schema, pdfDefaultSchemaMappings);
+ const transformed = await exporter.toReactPDFDocument(
+ partialBlocksToBlocksForTesting(schema, [
+ {
+ type: "columnList",
+ children: [
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "This paragraph is in a column!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 1.4,
+ },
+ children: [
+ {
+ type: "heading",
+ content: "So is this heading!",
+ },
+ ],
+ },
+ {
+ type: "column",
+ props: {
+ width: 0.8,
+ },
+ children: [
+ {
+ type: "paragraph",
+ content: "You can have multiple blocks in a column too",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 1",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 2",
+ },
+ {
+ type: "bulletListItem",
+ content: "Block 3",
+ },
+ ],
+ },
+ ],
+ },
+ ]),
+ );
+ const str = reactElementToJSXString(transformed);
+
+ await expect(str).toMatchFileSnapshot(
+ "__snapshots__/exampleWithMultiColumn.jsx",
+ );
+ });
});
diff --git a/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx b/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx
index c7cbe843f3..fb399f5ccf 100644
--- a/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx
+++ b/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx
@@ -146,9 +146,10 @@ export class PDFExporter<
b as any,
nestingLevel,
numberedListIndex,
+ children,
); // TODO: any
- if (b.type === "pageBreak") {
+ if (["pageBreak", "columnList", "column"].includes(b.type)) {
ret.push(self);
continue;
}
diff --git a/playground/src/examples.gen.tsx b/playground/src/examples.gen.tsx
index e136417a60..27f52c431a 100644
--- a/playground/src/examples.gen.tsx
+++ b/playground/src/examples.gen.tsx
@@ -964,6 +964,7 @@
],
"dependencies": {
"@blocknote/xl-pdf-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest",
"@react-pdf/renderer": "^4.3.0"
} as any,
"pro": true
@@ -987,6 +988,7 @@
],
"dependencies": {
"@blocknote/xl-docx-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest",
"docx": "^9.0.2"
} as any,
"pro": true
@@ -1009,7 +1011,8 @@
""
],
"dependencies": {
- "@blocknote/xl-odt-exporter": "latest"
+ "@blocknote/xl-odt-exporter": "latest",
+ "@blocknote/xl-multi-column": "latest"
} as any,
"pro": true
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c9557da304..24ab649d97 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1995,6 +1995,9 @@ importers:
'@blocknote/shadcn':
specifier: latest
version: link:../../../packages/shadcn
+ '@blocknote/xl-multi-column':
+ specifier: latest
+ version: link:../../../packages/xl-multi-column
'@blocknote/xl-pdf-exporter':
specifier: latest
version: link:../../../packages/xl-pdf-exporter
@@ -2041,6 +2044,9 @@ importers:
'@blocknote/xl-docx-exporter':
specifier: latest
version: link:../../../packages/xl-docx-exporter
+ '@blocknote/xl-multi-column':
+ specifier: latest
+ version: link:../../../packages/xl-multi-column
docx:
specifier: ^9.0.2
version: 9.3.0
@@ -2081,6 +2087,9 @@ importers:
'@blocknote/shadcn':
specifier: latest
version: link:../../../packages/shadcn
+ '@blocknote/xl-multi-column':
+ specifier: latest
+ version: link:../../../packages/xl-multi-column
'@blocknote/xl-odt-exporter':
specifier: latest
version: link:../../../packages/xl-odt-exporter
@@ -3992,6 +4001,9 @@ importers:
'@blocknote/core':
specifier: 0.33.0
version: link:../core
+ '@blocknote/xl-multi-column':
+ specifier: 0.33.0
+ version: link:../xl-multi-column
buffer:
specifier: ^6.0.3
version: 6.0.3
@@ -4169,6 +4181,9 @@ importers:
'@blocknote/core':
specifier: 0.33.0
version: link:../core
+ '@blocknote/xl-multi-column':
+ specifier: 0.33.0
+ version: link:../xl-multi-column
'@zip.js/zip.js':
specifier: ^2.7.57
version: 2.7.57
@@ -4221,6 +4236,9 @@ importers:
'@blocknote/react':
specifier: 0.33.0
version: link:../react
+ '@blocknote/xl-multi-column':
+ specifier: 0.33.0
+ version: link:../xl-multi-column
'@react-pdf/renderer':
specifier: ^4.3.0
version: 4.3.0(react@18.3.1)