Skip to content

Commit d9a4167

Browse files
authored
feat: support new table properties in PDF export (#1475)
1 parent 2ef89c7 commit d9a4167

File tree

5 files changed

+203
-51
lines changed

5 files changed

+203
-51
lines changed

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

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ export default function App() {
2929
// Creates a new editor instance with some initial content.
3030
const editor = useCreateBlockNote({
3131
schema: withPageBreak(BlockNoteSchema.create()),
32+
tables: {
33+
splitCells: true,
34+
cellBackgroundColor: true,
35+
cellTextColor: true,
36+
headers: true,
37+
},
3238
initialContent: [
3339
{
3440
type: "paragraph",

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

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export default function App() {
2525
// Creates a new editor instance with some initial content.
2626
const editor = useCreateBlockNote({
2727
schema: withPageBreak(BlockNoteSchema.create()),
28+
tables: {
29+
splitCells: true,
30+
cellBackgroundColor: true,
31+
cellTextColor: true,
32+
headers: true,
33+
},
2834
initialContent: [
2935
{
3036
type: "paragraph",

packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml

+48
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@
292292
<w:tc>
293293
<w:tcPr>
294294
<w:tcW w:type="dxa" w:w="150pt"/>
295+
<w:gridSpan w:val="1"/>
295296
</w:tcPr>
296297
<w:p>
297298
<w:r>
@@ -300,13 +301,19 @@
300301
</w:p>
301302
</w:tc>
302303
<w:tc>
304+
<w:tcPr>
305+
<w:gridSpan w:val="1"/>
306+
</w:tcPr>
303307
<w:p>
304308
<w:r>
305309
<w:t xml:space="preserve">Table Cell</w:t>
306310
</w:r>
307311
</w:p>
308312
</w:tc>
309313
<w:tc>
314+
<w:tcPr>
315+
<w:gridSpan w:val="1"/>
316+
</w:tcPr>
310317
<w:p>
311318
<w:r>
312319
<w:t xml:space="preserve">Table Cell</w:t>
@@ -318,6 +325,7 @@
318325
<w:tc>
319326
<w:tcPr>
320327
<w:tcW w:type="dxa" w:w="150pt"/>
328+
<w:gridSpan w:val="1"/>
321329
</w:tcPr>
322330
<w:p>
323331
<w:r>
@@ -326,13 +334,19 @@
326334
</w:p>
327335
</w:tc>
328336
<w:tc>
337+
<w:tcPr>
338+
<w:gridSpan w:val="1"/>
339+
</w:tcPr>
329340
<w:p>
330341
<w:r>
331342
<w:t xml:space="preserve">Table Cell</w:t>
332343
</w:r>
333344
</w:p>
334345
</w:tc>
335346
<w:tc>
347+
<w:tcPr>
348+
<w:gridSpan w:val="1"/>
349+
</w:tcPr>
336350
<w:p>
337351
<w:r>
338352
<w:t xml:space="preserve">Table Cell</w:t>
@@ -344,6 +358,7 @@
344358
<w:tc>
345359
<w:tcPr>
346360
<w:tcW w:type="dxa" w:w="150pt"/>
361+
<w:gridSpan w:val="1"/>
347362
</w:tcPr>
348363
<w:p>
349364
<w:r>
@@ -352,13 +367,19 @@
352367
</w:p>
353368
</w:tc>
354369
<w:tc>
370+
<w:tcPr>
371+
<w:gridSpan w:val="1"/>
372+
</w:tcPr>
355373
<w:p>
356374
<w:r>
357375
<w:t xml:space="preserve">Table Cell</w:t>
358376
</w:r>
359377
</w:p>
360378
</w:tc>
361379
<w:tc>
380+
<w:tcPr>
381+
<w:gridSpan w:val="1"/>
382+
</w:tcPr>
362383
<w:p>
363384
<w:r>
364385
<w:t xml:space="preserve">Table Cell</w:t>
@@ -599,20 +620,29 @@
599620
</w:tblGrid>
600621
<w:tr>
601622
<w:tc>
623+
<w:tcPr>
624+
<w:gridSpan w:val="1"/>
625+
</w:tcPr>
602626
<w:p>
603627
<w:r>
604628
<w:t xml:space="preserve">Table Cell 1</w:t>
605629
</w:r>
606630
</w:p>
607631
</w:tc>
608632
<w:tc>
633+
<w:tcPr>
634+
<w:gridSpan w:val="1"/>
635+
</w:tcPr>
609636
<w:p>
610637
<w:r>
611638
<w:t xml:space="preserve">Table Cell 2</w:t>
612639
</w:r>
613640
</w:p>
614641
</w:tc>
615642
<w:tc>
643+
<w:tcPr>
644+
<w:gridSpan w:val="1"/>
645+
</w:tcPr>
616646
<w:p>
617647
<w:r>
618648
<w:t xml:space="preserve">Table Cell 3</w:t>
@@ -622,13 +652,19 @@
622652
</w:tr>
623653
<w:tr>
624654
<w:tc>
655+
<w:tcPr>
656+
<w:gridSpan w:val="1"/>
657+
</w:tcPr>
625658
<w:p>
626659
<w:r>
627660
<w:t xml:space="preserve">Table Cell 4</w:t>
628661
</w:r>
629662
</w:p>
630663
</w:tc>
631664
<w:tc>
665+
<w:tcPr>
666+
<w:gridSpan w:val="1"/>
667+
</w:tcPr>
632668
<w:p>
633669
<w:r>
634670
<w:rPr>
@@ -640,6 +676,9 @@
640676
</w:p>
641677
</w:tc>
642678
<w:tc>
679+
<w:tcPr>
680+
<w:gridSpan w:val="1"/>
681+
</w:tcPr>
643682
<w:p>
644683
<w:r>
645684
<w:t xml:space="preserve">Table Cell 6</w:t>
@@ -649,20 +688,29 @@
649688
</w:tr>
650689
<w:tr>
651690
<w:tc>
691+
<w:tcPr>
692+
<w:gridSpan w:val="1"/>
693+
</w:tcPr>
652694
<w:p>
653695
<w:r>
654696
<w:t xml:space="preserve">Table Cell 7</w:t>
655697
</w:r>
656698
</w:p>
657699
</w:tc>
658700
<w:tc>
701+
<w:tcPr>
702+
<w:gridSpan w:val="1"/>
703+
</w:tcPr>
659704
<w:p>
660705
<w:r>
661706
<w:t xml:space="preserve">Table Cell 8</w:t>
662707
</w:r>
663708
</w:p>
664709
</w:tc>
665710
<w:tc>
711+
<w:tcPr>
712+
<w:gridSpan w:val="1"/>
713+
</w:tcPr>
666714
<w:p>
667715
<w:r>
668716
<w:t xml:space="preserve">Table Cell 9</w:t>
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
import { Exporter, InlineContentSchema, TableContent } from "@blocknote/core";
1+
import {
2+
Exporter,
3+
InlineContentSchema,
4+
mapTableCell,
5+
TableContent,
6+
UnreachableCaseError,
7+
} from "@blocknote/core";
28
import {
39
Table as DocxTable,
410
Paragraph,
511
ParagraphChild,
12+
ShadingType,
613
TableCell,
714
TableRow,
815
} from "docx";
@@ -12,33 +19,84 @@ export const Table = (
1219
t: Exporter<any, any, any, any, ParagraphChild, any, any>
1320
) => {
1421
const DEFAULT_COLUMN_WIDTH = 120;
22+
23+
// If headerRows is 1, then the first row is a header row
24+
const headerRows = new Array(data.headerRows ?? 0).fill(true);
25+
// If headerCols is 1, then the first column is a header column
26+
const headerCols = new Array(data.headerCols ?? 0).fill(true);
27+
1528
return new DocxTable({
1629
layout: "autofit",
1730
columnWidths: data.columnWidths.map(
1831
(w) =>
1932
(w ?? DEFAULT_COLUMN_WIDTH) * /* to points */ 0.75 * /* to twips */ 20
2033
),
21-
rows: data.rows.map(
22-
(row) =>
23-
new TableRow({
24-
children: row.cells.map((cell, i) => {
25-
const width = data.columnWidths?.[i];
26-
return new TableCell({
27-
width: width
28-
? {
29-
size: `${width * 0.75}pt`,
30-
type: "dxa",
31-
}
32-
: undefined,
33-
children: [
34-
new Paragraph({
35-
// TODO: fix this
36-
children: t.transformInlineContent(cell as any),
37-
}),
38-
],
39-
});
40-
}),
41-
})
42-
),
34+
rows: data.rows.map((row, rowIndex) => {
35+
const isHeaderRow = headerRows[rowIndex];
36+
return new TableRow({
37+
tableHeader: isHeaderRow,
38+
children: row.cells.map((c, colIndex) => {
39+
const width = data.columnWidths?.[colIndex];
40+
const cell = mapTableCell(c);
41+
const isHeaderColumn = headerCols[colIndex];
42+
43+
return new TableCell({
44+
width: width
45+
? {
46+
size: `${width * 0.75}pt`,
47+
type: "dxa",
48+
}
49+
: undefined,
50+
columnSpan: cell.props.colspan,
51+
rowSpan: cell.props.rowspan,
52+
shading:
53+
cell.props.backgroundColor === "default" ||
54+
!cell.props.backgroundColor
55+
? undefined
56+
: {
57+
type: ShadingType.SOLID,
58+
color:
59+
t.options.colors[
60+
cell.props
61+
.backgroundColor as keyof typeof t.options.colors
62+
].background.slice(1),
63+
},
64+
children: [
65+
new Paragraph({
66+
children: t.transformInlineContent(cell.content),
67+
68+
alignment:
69+
!cell.props.textAlignment ||
70+
cell.props.textAlignment === "left"
71+
? undefined
72+
: cell.props.textAlignment === "center"
73+
? "center"
74+
: cell.props.textAlignment === "right"
75+
? "right"
76+
: cell.props.textAlignment === "justify"
77+
? "distribute"
78+
: (() => {
79+
throw new UnreachableCaseError(
80+
cell.props.textAlignment
81+
);
82+
})(),
83+
run: {
84+
// TODO add support for table headers exporting, bolding seems to not be working at the moment
85+
bold: isHeaderRow || isHeaderColumn,
86+
// TODO table paragraph color seems to not be working at the moment
87+
// Probably because the runs are setting their own color
88+
color:
89+
cell.props.textColor === "default" || !cell.props.textColor
90+
? undefined
91+
: t.options.colors[
92+
cell.props.textColor as keyof typeof t.options.colors
93+
].text.slice(1),
94+
},
95+
}),
96+
],
97+
});
98+
}),
99+
});
100+
}),
43101
});
44102
};

0 commit comments

Comments
 (0)