Skip to content

Commit 9b6f462

Browse files
authored
feat: support multi-column block in PDF, DOCX & ODT exporters (#1781)
1 parent 0c35a3f commit 9b6f462

File tree

34 files changed

+2659
-89
lines changed

34 files changed

+2659
-89
lines changed

examples/05-interoperability/05-converting-blocks-to-pdf/.bnexample.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"tags": ["Interoperability"],
66
"dependencies": {
77
"@blocknote/xl-pdf-exporter": "latest",
8+
"@blocknote/xl-multi-column": "latest",
89
"@react-pdf/renderer": "^4.3.0"
910
},
1011
"pro": true

examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
filterSuggestionItems,
55
withPageBreak,
66
} from "@blocknote/core";
7+
import * as locales from "@blocknote/core/locales";
78
import "@blocknote/core/fonts/inter.css";
89
import { BlockNoteView } from "@blocknote/mantine";
910
import "@blocknote/mantine/style.css";
@@ -17,6 +18,12 @@ import {
1718
PDFExporter,
1819
pdfDefaultSchemaMappings,
1920
} from "@blocknote/xl-pdf-exporter";
21+
import {
22+
getMultiColumnSlashMenuItems,
23+
multiColumnDropCursor,
24+
locales as multiColumnLocales,
25+
withMultiColumn,
26+
} from "@blocknote/xl-multi-column";
2027
import { PDFViewer } from "@react-pdf/renderer";
2128
import { useEffect, useMemo, useState } from "react";
2229

@@ -28,7 +35,12 @@ export default function App() {
2835

2936
// Creates a new editor instance with some initial content.
3037
const editor = useCreateBlockNote({
31-
schema: withPageBreak(BlockNoteSchema.create()),
38+
schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())),
39+
dropCursor: multiColumnDropCursor,
40+
dictionary: {
41+
...locales.en,
42+
multi_column: multiColumnLocales.en,
43+
},
3244
tables: {
3345
splitCells: true,
3446
cellBackgroundColor: true,
@@ -313,9 +325,72 @@ export default function App() {
313325
console.log("Hello World", message);
314326
};`,
315327
},
328+
{
329+
type: "columnList",
330+
children: [
331+
{
332+
type: "column",
333+
props: {
334+
width: 0.8,
335+
},
336+
children: [
337+
{
338+
type: "paragraph",
339+
content: "This paragraph is in a column!",
340+
},
341+
],
342+
},
343+
{
344+
type: "column",
345+
props: {
346+
width: 1.4,
347+
},
348+
children: [
349+
{
350+
type: "heading",
351+
content: "So is this heading!",
352+
},
353+
],
354+
},
355+
{
356+
type: "column",
357+
props: {
358+
width: 0.8,
359+
},
360+
children: [
361+
{
362+
type: "paragraph",
363+
content: "You can have multiple blocks in a column too",
364+
},
365+
{
366+
type: "bulletListItem",
367+
content: "Block 1",
368+
},
369+
{
370+
type: "bulletListItem",
371+
content: "Block 2",
372+
},
373+
{
374+
type: "bulletListItem",
375+
content: "Block 3",
376+
},
377+
],
378+
},
379+
],
380+
},
316381
],
317382
});
318-
383+
const getSlashMenuItems = useMemo(() => {
384+
return async (query: string) =>
385+
filterSuggestionItems(
386+
combineByGroup(
387+
getDefaultReactSlashMenuItems(editor),
388+
getPageBreakReactSlashMenuItems(editor),
389+
getMultiColumnSlashMenuItems(editor),
390+
),
391+
query,
392+
);
393+
}, [editor]);
319394
const onChange = async () => {
320395
const exporter = new PDFExporter(editor.schema, pdfDefaultSchemaMappings);
321396
// Converts the editor's contents from Block objects to HTML and store to state.
@@ -330,23 +405,14 @@ export default function App() {
330405
// eslint-disable-next-line react-hooks/exhaustive-deps
331406
}, []);
332407

333-
const slashMenuItems = useMemo(() => {
334-
return combineByGroup(
335-
getDefaultReactSlashMenuItems(editor),
336-
getPageBreakReactSlashMenuItems(editor),
337-
);
338-
}, [editor]);
339-
340408
// Renders the editor instance, and its contents as HTML below.
341409
return (
342410
<div className="wrapper">
343411
<div className="editor">
344412
<BlockNoteView editor={editor} slashMenu={false} onChange={onChange}>
345413
<SuggestionMenuController
346414
triggerCharacter={"/"}
347-
getItems={async (query) =>
348-
filterSuggestionItems(slashMenuItems, query)
349-
}
415+
getItems={getSlashMenuItems}
350416
/>
351417
</BlockNoteView>
352418
</div>

examples/05-interoperability/05-converting-blocks-to-pdf/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"react": "^18.3.1",
1919
"react-dom": "^18.3.1",
2020
"@blocknote/xl-pdf-exporter": "latest",
21+
"@blocknote/xl-multi-column": "latest",
2122
"@react-pdf/renderer": "^4.3.0"
2223
},
2324
"devDependencies": {

examples/05-interoperability/06-converting-blocks-to-docx/.bnexample.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"tags": [""],
66
"dependencies": {
77
"@blocknote/xl-docx-exporter": "latest",
8+
"@blocknote/xl-multi-column": "latest",
89
"docx": "^9.0.2"
910
},
1011
"pro": true

examples/05-interoperability/06-converting-blocks-to-docx/App.tsx

Lines changed: 79 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
filterSuggestionItems,
55
withPageBreak,
66
} from "@blocknote/core";
7+
import * as locales from "@blocknote/core/locales";
78
import "@blocknote/core/fonts/inter.css";
89
import { BlockNoteView } from "@blocknote/mantine";
910
import "@blocknote/mantine/style.css";
@@ -17,14 +18,25 @@ import {
1718
DOCXExporter,
1819
docxDefaultSchemaMappings,
1920
} from "@blocknote/xl-docx-exporter";
21+
import {
22+
getMultiColumnSlashMenuItems,
23+
multiColumnDropCursor,
24+
locales as multiColumnLocales,
25+
withMultiColumn,
26+
} from "@blocknote/xl-multi-column";
2027
import { useMemo } from "react";
2128

2229
import "./styles.css";
2330

2431
export default function App() {
2532
// Creates a new editor instance with some initial content.
2633
const editor = useCreateBlockNote({
27-
schema: withPageBreak(BlockNoteSchema.create()),
34+
schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())),
35+
dropCursor: multiColumnDropCursor,
36+
dictionary: {
37+
...locales.en,
38+
multi_column: multiColumnLocales.en,
39+
},
2840
tables: {
2941
splitCells: true,
3042
cellBackgroundColor: true,
@@ -309,9 +321,73 @@ export default function App() {
309321
console.log("Hello World", message);
310322
};`,
311323
},
324+
325+
{
326+
type: "columnList",
327+
children: [
328+
{
329+
type: "column",
330+
props: {
331+
width: 0.8,
332+
},
333+
children: [
334+
{
335+
type: "paragraph",
336+
content: "This paragraph is in a column!",
337+
},
338+
],
339+
},
340+
{
341+
type: "column",
342+
props: {
343+
width: 1.4,
344+
},
345+
children: [
346+
{
347+
type: "heading",
348+
content: "So is this heading!",
349+
},
350+
],
351+
},
352+
{
353+
type: "column",
354+
props: {
355+
width: 0.8,
356+
},
357+
children: [
358+
{
359+
type: "paragraph",
360+
content: "You can have multiple blocks in a column too",
361+
},
362+
{
363+
type: "bulletListItem",
364+
content: "Block 1",
365+
},
366+
{
367+
type: "bulletListItem",
368+
content: "Block 2",
369+
},
370+
{
371+
type: "bulletListItem",
372+
content: "Block 3",
373+
},
374+
],
375+
},
376+
],
377+
},
312378
],
313379
});
314-
380+
const getSlashMenuItems = useMemo(() => {
381+
return async (query: string) =>
382+
filterSuggestionItems(
383+
combineByGroup(
384+
getDefaultReactSlashMenuItems(editor),
385+
getPageBreakReactSlashMenuItems(editor),
386+
getMultiColumnSlashMenuItems(editor),
387+
),
388+
query,
389+
);
390+
}, [editor]);
315391
const onDownloadClick = async () => {
316392
const exporter = new DOCXExporter(editor.schema, docxDefaultSchemaMappings);
317393

@@ -332,13 +408,6 @@ export default function App() {
332408
window.URL.revokeObjectURL(link.href);
333409
};
334410

335-
const slashMenuItems = useMemo(() => {
336-
return combineByGroup(
337-
getDefaultReactSlashMenuItems(editor),
338-
getPageBreakReactSlashMenuItems(editor),
339-
);
340-
}, [editor]);
341-
342411
// Renders the editor instance, and its contents as HTML below.
343412
return (
344413
<div>
@@ -351,9 +420,7 @@ export default function App() {
351420
<BlockNoteView editor={editor} slashMenu={false}>
352421
<SuggestionMenuController
353422
triggerCharacter={"/"}
354-
getItems={async (query) =>
355-
filterSuggestionItems(slashMenuItems, query)
356-
}
423+
getItems={getSlashMenuItems}
357424
/>
358425
</BlockNoteView>
359426
</div>

examples/05-interoperability/06-converting-blocks-to-docx/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"react": "^18.3.1",
1919
"react-dom": "^18.3.1",
2020
"@blocknote/xl-docx-exporter": "latest",
21+
"@blocknote/xl-multi-column": "latest",
2122
"docx": "^9.0.2"
2223
},
2324
"devDependencies": {

examples/05-interoperability/07-converting-blocks-to-odt/.bnexample.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"author": "areknawo",
55
"tags": [""],
66
"dependencies": {
7-
"@blocknote/xl-odt-exporter": "latest"
7+
"@blocknote/xl-odt-exporter": "latest",
8+
"@blocknote/xl-multi-column": "latest"
89
},
910
"pro": true
1011
}

0 commit comments

Comments
 (0)