diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx
index 2bb97a4b..577c0176 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-editor/types.tsx
@@ -17,8 +17,12 @@ export type HeadingBlock = {
};
};
+export type DocsBlockSchema = typeof blockNoteSchema.blockSchema;
+export type DocsInlineContentSchema =
+ typeof blockNoteSchema.inlineContentSchema;
+export type DocsStyleSchema = typeof blockNoteSchema.styleSchema;
export type DocsBlockNoteEditor = BlockNoteEditor<
- typeof blockNoteSchema.blockSchema,
- typeof blockNoteSchema.inlineContentSchema,
- typeof blockNoteSchema.styleSchema
+ DocsBlockSchema,
+ DocsInlineContentSchema,
+ DocsStyleSchema
>;
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/headingPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/headingPDF.tsx
new file mode 100644
index 00000000..fca81d7b
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/headingPDF.tsx
@@ -0,0 +1,25 @@
+import { Text } from '@react-pdf/renderer';
+
+import { DocsExporterPDF } from '../types';
+
+export const blockMappingHeadingPDF: DocsExporterPDF['mappings']['blockMapping']['heading'] =
+ (block, exporter) => {
+ const PIXELS_PER_POINT = 0.75;
+ const MERGE_RATIO = 7.5;
+ const FONT_SIZE = 16;
+ const fontSizeEM =
+ block.props.level === 1 ? 2 : block.props.level === 2 ? 1.5 : 1.17;
+ return (
+
+ {exporter.transformInlineContent(block.content)}
+
+ );
+ };
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts
new file mode 100644
index 00000000..36f9c79f
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts
@@ -0,0 +1,3 @@
+export * from './headingPDF';
+export * from './paragraphPDF';
+export * from './tablePDF';
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx
new file mode 100644
index 00000000..86d74877
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/paragraphPDF.tsx
@@ -0,0 +1,31 @@
+import { Text } from '@react-pdf/renderer';
+
+import { DocsExporterPDF } from '../types';
+
+export const blockMappingParagraphPDF: DocsExporterPDF['mappings']['blockMapping']['paragraph'] =
+ (block, exporter) => {
+ /**
+ * Breakline in the editor are not rendered in the PDF
+ * By adding a space if the block is empty we ensure that the block is rendered
+ */
+ if (Array.isArray(block.content)) {
+ block.content.forEach((content) => {
+ if (content.type === 'text' && !content.text) {
+ content.text = ' ';
+ }
+ });
+
+ if (!block.content.length) {
+ block.content.push({
+ styles: {},
+ text: ' ',
+ type: 'text',
+ });
+ }
+ }
+ return (
+
+ {exporter.transformInlineContent(block.content)}
+
+ );
+ };
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/tablePDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/tablePDF.tsx
new file mode 100644
index 00000000..61117925
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/tablePDF.tsx
@@ -0,0 +1,52 @@
+import { TD, TH, TR, Table } from '@ag-media/react-pdf-table';
+import { View } from '@react-pdf/renderer';
+
+import { DocsExporterPDF } from '../types';
+
+export const blockMappingTablePDF: DocsExporterPDF['mappings']['blockMapping']['table'] =
+ (block, exporter) => {
+ return (
+
+ {block.content.rows.map((row, index) => {
+ if (index === 0) {
+ return (
+ |
+ {row.cells.map((cell, index) => {
+ // Make empty cells are rendered.
+ if (cell.length === 0) {
+ cell.push({
+ styles: {},
+ text: ' ',
+ type: 'text',
+ });
+ }
+ return (
+ | {exporter.transformInlineContent(cell)} |
+ );
+ })}
+
+ );
+ }
+ return (
+
+ {row.cells.map((cell, index) => {
+ // Make empty cells are rendered.
+ if (cell.length === 0) {
+ cell.push({
+ styles: {},
+ text: ' ',
+ type: 'text',
+ });
+ }
+ return (
+ |
+ {exporter.transformInlineContent(cell)}
+ |
+ );
+ })}
+
+ );
+ })}
+
+ );
+ };
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
index bdad68d1..a2ba6f8a 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/components/ModalExport.tsx
@@ -1,11 +1,5 @@
-import {
- DOCXExporter,
- docxDefaultSchemaMappings,
-} from '@blocknote/xl-docx-exporter';
-import {
- PDFExporter,
- pdfDefaultSchemaMappings,
-} from '@blocknote/xl-pdf-exporter';
+import { DOCXExporter } from '@blocknote/xl-docx-exporter';
+import { PDFExporter } from '@blocknote/xl-pdf-exporter';
import {
Button,
Loader,
@@ -15,7 +9,7 @@ import {
VariantType,
useToastProvider,
} from '@openfun/cunningham-react';
-import { Text as PDFText, pdf } from '@react-pdf/renderer';
+import { pdf } from '@react-pdf/renderer';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
@@ -25,10 +19,10 @@ import { useEditorStore } from '@/features/docs/doc-editor';
import { Doc, useTrans } from '@/features/docs/doc-management';
import { TemplatesOrdering, useTemplates } from '../api/useTemplates';
+import { docxDocsSchemaMappings } from '../mappingDocx';
+import { pdfDocsSchemaMappings } from '../mappingPDF';
import { downloadFile, exportResolveFileUrl } from '../utils';
-import { Table } from './blocks/Table';
-
enum DocDownloadFormat {
PDF = 'pdf',
DOCX = 'docx',
@@ -96,91 +90,25 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
if (format === DocDownloadFormat.PDF) {
const defaultExporter = new PDFExporter(
editor.schema,
- pdfDefaultSchemaMappings,
+ pdfDocsSchemaMappings,
);
- const exporter = new PDFExporter(
- editor.schema,
- {
- ...pdfDefaultSchemaMappings,
- blockMapping: {
- ...pdfDefaultSchemaMappings.blockMapping,
- heading: (block, exporter) => {
- const PIXELS_PER_POINT = 0.75;
- const MERGE_RATIO = 7.5;
- const FONT_SIZE = 16;
- const fontSizeEM =
- block.props.level === 1
- ? 2
- : block.props.level === 2
- ? 1.5
- : 1.17;
- return (
-
- {exporter.transformInlineContent(block.content)}
-
- );
- },
- paragraph: (block, exporter) => {
- /**
- * Breakline in the editor are not rendered in the PDF
- * By adding a space if the block is empty we ensure that the block is rendered
- */
- if (Array.isArray(block.content)) {
- block.content.forEach((content) => {
- if (content.type === 'text' && !content.text) {
- content.text = ' ';
- }
- });
-
- if (!block.content.length) {
- block.content.push({
- styles: {},
- text: ' ',
- type: 'text',
- });
- }
- }
- return (
-
- {exporter.transformInlineContent(block.content)}
-
- );
- },
- table: (block, transformer) => {
- return ;
- },
- },
- },
- {
- resolveFileUrl: async (url) =>
- exportResolveFileUrl(url, defaultExporter.options.resolveFileUrl),
- },
- );
+ const exporter = new PDFExporter(editor.schema, pdfDocsSchemaMappings, {
+ resolveFileUrl: async (url) =>
+ exportResolveFileUrl(url, defaultExporter.options.resolveFileUrl),
+ });
const pdfDocument = await exporter.toReactPDFDocument(exportDocument);
blobExport = await pdf(pdfDocument).toBlob();
} else {
const defaultExporter = new DOCXExporter(
editor.schema,
- docxDefaultSchemaMappings,
+ docxDocsSchemaMappings,
);
- const exporter = new DOCXExporter(
- editor.schema,
- docxDefaultSchemaMappings,
- {
- resolveFileUrl: async (url) =>
- exportResolveFileUrl(url, defaultExporter.options.resolveFileUrl),
- },
- );
+ const exporter = new DOCXExporter(editor.schema, docxDocsSchemaMappings, {
+ resolveFileUrl: async (url) =>
+ exportResolveFileUrl(url, defaultExporter.options.resolveFileUrl),
+ });
blobExport = await exporter.toBlob(exportDocument);
}
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/components/blocks/Table.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/components/blocks/Table.tsx
deleted file mode 100644
index 156c3eb6..00000000
--- a/src/frontend/apps/impress/src/features/docs/doc-export/components/blocks/Table.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-import { TD, TH, TR, Table as TablePDF } from '@ag-media/react-pdf-table';
-import {
- DefaultBlockSchema,
- Exporter,
- InlineContentSchema,
- StyleSchema,
- TableContent,
-} from '@blocknote/core';
-import { View } from '@react-pdf/renderer';
-import { ReactNode } from 'react';
-
-export const Table = (props: {
- data: TableContent;
- transformer: Exporter<
- DefaultBlockSchema,
- InlineContentSchema,
- StyleSchema,
- unknown,
- unknown,
- unknown,
- unknown
- >;
-}) => {
- return (
-
- {props.data.rows.map((row, index) => {
- if (index === 0) {
- return (
-
- {row.cells.map((cell, index) => {
- // Make empty cells are rendered.
- if (cell.length === 0) {
- cell.push({
- styles: {},
- text: ' ',
- type: 'text',
- });
- }
- return (
- |
- {props.transformer.transformInlineContent(cell)}
- |
- );
- })}
-
- );
- }
- return (
-
- {row.cells.map((cell, index) => {
- // Make empty cells are rendered.
- if (cell.length === 0) {
- cell.push({
- styles: {},
- text: ' ',
- type: 'text',
- });
- }
- return (
- |
-
- {
- props.transformer.transformInlineContent(
- cell,
- ) as ReactNode
- }
-
- |
- );
- })}
-
- );
- })}
-
- );
-};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx
new file mode 100644
index 00000000..68b90803
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx
@@ -0,0 +1,10 @@
+import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter';
+
+import { DocsExporterDocx } from './types';
+
+export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = {
+ ...docxDefaultSchemaMappings,
+ blockMapping: {
+ ...docxDefaultSchemaMappings.blockMapping,
+ },
+};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx
new file mode 100644
index 00000000..19c9f204
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx
@@ -0,0 +1,18 @@
+import { pdfDefaultSchemaMappings } from '@blocknote/xl-pdf-exporter';
+
+import {
+ blockMappingHeadingPDF,
+ blockMappingParagraphPDF,
+ blockMappingTablePDF,
+} from './blocks-mapping';
+import { DocsExporterPDF } from './types';
+
+export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
+ ...pdfDefaultSchemaMappings,
+ blockMapping: {
+ ...pdfDefaultSchemaMappings.blockMapping,
+ heading: blockMappingHeadingPDF,
+ paragraph: blockMappingParagraphPDF,
+ table: blockMappingTablePDF,
+ },
+};
diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/types.ts b/src/frontend/apps/impress/src/features/docs/doc-export/types.ts
index 70b62e56..42f8c156 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-export/types.ts
+++ b/src/frontend/apps/impress/src/features/docs/doc-export/types.ts
@@ -1,3 +1,18 @@
+import { Exporter } from '@blocknote/core';
+import { Link, Text, TextProps } from '@react-pdf/renderer';
+import {
+ IRunPropertiesOptions,
+ Paragraph,
+ ParagraphChild,
+ Table,
+ TextRun,
+} from 'docx';
+
+import {
+ DocsBlockSchema,
+ DocsInlineContentSchema,
+ DocsStyleSchema,
+} from '../doc-editor';
import { Access } from '../doc-management';
export interface Template {
@@ -16,3 +31,23 @@ export interface Template {
css: string;
code: string;
}
+
+export type DocsExporterPDF = Exporter<
+ NoInfer,
+ NoInfer,
+ NoInfer,
+ React.ReactElement,
+ React.ReactElement | React.ReactElement,
+ TextProps['style'],
+ React.ReactElement
+>;
+
+export type DocsExporterDocx = Exporter<
+ NoInfer,
+ NoInfer,
+ NoInfer,
+ Promise | Paragraph[] | Paragraph | Table,
+ ParagraphChild,
+ IRunPropertiesOptions,
+ TextRun
+>;