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 269cc81c9a..a163fa1c8b 100644 --- a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx +++ b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx @@ -29,6 +29,12 @@ export default function App() { // Creates a new editor instance with some initial content. const editor = useCreateBlockNote({ schema: withPageBreak(BlockNoteSchema.create()), + tables: { + splitCells: true, + cellBackgroundColor: true, + cellTextColor: true, + headers: true, + }, initialContent: [ { type: "paragraph", 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 94bb8d8c91..9ab9a0b0de 100644 --- a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx +++ b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx @@ -25,6 +25,12 @@ export default function App() { // Creates a new editor instance with some initial content. const editor = useCreateBlockNote({ schema: withPageBreak(BlockNoteSchema.create()), + tables: { + splitCells: true, + cellBackgroundColor: true, + cellTextColor: true, + headers: true, + }, initialContent: [ { type: "paragraph", 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 ccd1ff2147..15e743e52e 100644 --- a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml +++ b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml @@ -292,6 +292,7 @@ + @@ -300,6 +301,9 @@ + + + Table Cell @@ -307,6 +311,9 @@ + + + Table Cell @@ -318,6 +325,7 @@ + @@ -326,6 +334,9 @@ + + + Table Cell @@ -333,6 +344,9 @@ + + + Table Cell @@ -344,6 +358,7 @@ + @@ -352,6 +367,9 @@ + + + Table Cell @@ -359,6 +377,9 @@ + + + Table Cell @@ -599,6 +620,9 @@ + + + Table Cell 1 @@ -606,6 +630,9 @@ + + + Table Cell 2 @@ -613,6 +640,9 @@ + + + Table Cell 3 @@ -622,6 +652,9 @@ + + + Table Cell 4 @@ -629,6 +662,9 @@ + + + @@ -640,6 +676,9 @@ + + + Table Cell 6 @@ -649,6 +688,9 @@ + + + Table Cell 7 @@ -656,6 +698,9 @@ + + + Table Cell 8 @@ -663,6 +708,9 @@ + + + Table Cell 9 diff --git a/packages/xl-docx-exporter/src/docx/util/Table.tsx b/packages/xl-docx-exporter/src/docx/util/Table.tsx index 71db4f24af..9d1e084fdc 100644 --- a/packages/xl-docx-exporter/src/docx/util/Table.tsx +++ b/packages/xl-docx-exporter/src/docx/util/Table.tsx @@ -1,8 +1,15 @@ -import { Exporter, InlineContentSchema, TableContent } from "@blocknote/core"; +import { + Exporter, + InlineContentSchema, + mapTableCell, + TableContent, + UnreachableCaseError, +} from "@blocknote/core"; import { Table as DocxTable, Paragraph, ParagraphChild, + ShadingType, TableCell, TableRow, } from "docx"; @@ -12,33 +19,84 @@ export const Table = ( t: Exporter ) => { const DEFAULT_COLUMN_WIDTH = 120; + + // If headerRows is 1, then the first row is a header row + const headerRows = new Array(data.headerRows ?? 0).fill(true); + // If headerCols is 1, then the first column is a header column + const headerCols = new Array(data.headerCols ?? 0).fill(true); + return new DocxTable({ layout: "autofit", columnWidths: data.columnWidths.map( (w) => (w ?? DEFAULT_COLUMN_WIDTH) * /* to points */ 0.75 * /* to twips */ 20 ), - rows: data.rows.map( - (row) => - new TableRow({ - children: row.cells.map((cell, i) => { - const width = data.columnWidths?.[i]; - return new TableCell({ - width: width - ? { - size: `${width * 0.75}pt`, - type: "dxa", - } - : undefined, - children: [ - new Paragraph({ - // TODO: fix this - children: t.transformInlineContent(cell as any), - }), - ], - }); - }), - }) - ), + rows: data.rows.map((row, rowIndex) => { + const isHeaderRow = headerRows[rowIndex]; + return new TableRow({ + tableHeader: isHeaderRow, + children: row.cells.map((c, colIndex) => { + const width = data.columnWidths?.[colIndex]; + const cell = mapTableCell(c); + const isHeaderColumn = headerCols[colIndex]; + + return new TableCell({ + width: width + ? { + size: `${width * 0.75}pt`, + type: "dxa", + } + : undefined, + columnSpan: cell.props.colspan, + rowSpan: cell.props.rowspan, + shading: + cell.props.backgroundColor === "default" || + !cell.props.backgroundColor + ? undefined + : { + type: ShadingType.SOLID, + color: + t.options.colors[ + cell.props + .backgroundColor as keyof typeof t.options.colors + ].background.slice(1), + }, + children: [ + new Paragraph({ + children: t.transformInlineContent(cell.content), + + alignment: + !cell.props.textAlignment || + cell.props.textAlignment === "left" + ? undefined + : cell.props.textAlignment === "center" + ? "center" + : cell.props.textAlignment === "right" + ? "right" + : cell.props.textAlignment === "justify" + ? "distribute" + : (() => { + throw new UnreachableCaseError( + cell.props.textAlignment + ); + })(), + run: { + // TODO add support for table headers exporting, bolding seems to not be working at the moment + bold: isHeaderRow || isHeaderColumn, + // TODO table paragraph color seems to not be working at the moment + // Probably because the runs are setting their own color + color: + cell.props.textColor === "default" || !cell.props.textColor + ? undefined + : t.options.colors[ + cell.props.textColor as keyof typeof t.options.colors + ].text.slice(1), + }, + }), + ], + }); + }), + }); + }), }); }; diff --git a/packages/xl-pdf-exporter/src/pdf/util/table/Table.tsx b/packages/xl-pdf-exporter/src/pdf/util/table/Table.tsx index 56991d330d..f7c5ef03b5 100644 --- a/packages/xl-pdf-exporter/src/pdf/util/table/Table.tsx +++ b/packages/xl-pdf-exporter/src/pdf/util/table/Table.tsx @@ -30,6 +30,9 @@ const styles = StyleSheet.create({ wordWrap: "break-word", whiteSpace: "pre-wrap", }, + headerCell: { + fontWeight: "bold", + }, bottomCell: { borderBottom: "1px solid #ddd", }, @@ -49,31 +52,62 @@ export const Table = (props: { any, any >; -}) => ( - - {props.data.rows.map((row, index) => ( - - {row.cells.map((cell, index) => ( - - {props.transformer.transformInlineContent( - mapTableCell(cell).content - )} - - ))} - - ))} - -); +}) => { + // If headerRows is 1, then the first row is a header row + const headerRows = new Array(props.data.headerRows ?? 0).fill(true); + // If headerCols is 1, then the first column is a header column + const headerCols = new Array(props.data.headerCols ?? 0).fill(true); + + return ( + + {props.data.rows.map((row, rowIndex) => ( + + {row.cells.map((c, colIndex) => { + const cell = mapTableCell(c); + + const isHeaderRow = headerRows[rowIndex]; + const isHeaderCol = headerCols[colIndex]; + + // TODO we need to support for colspan and rowspan, but at the moment are blocked by react-pdf + return ( + + {props.transformer.transformInlineContent(cell.content)} + + ); + })} + + ))} + + ); +};