1+ import { SpecificBlock } from '@blocknote/core' ;
12import { DOCXExporter } from '@blocknote/xl-docx-exporter' ;
23import { ODTExporter } from '@blocknote/xl-odt-exporter' ;
34import { PDFExporter } from '@blocknote/xl-pdf-exporter' ;
45import {
56 Button ,
7+ Checkbox ,
68 Loader ,
79 Modal ,
810 ModalSize ,
@@ -20,9 +22,15 @@ import { css } from 'styled-components';
2022
2123import { Box , ButtonCloseModal , Text } from '@/components' ;
2224import { useMediaUrl } from '@/core' ;
23- import { useEditorStore } from '@/docs/doc-editor' ;
25+ import {
26+ DocsBlockSchema ,
27+ DocsInlineContentSchema ,
28+ DocsStyleSchema ,
29+ useEditorStore ,
30+ } from '@/docs/doc-editor' ;
2431import { Doc , useTrans } from '@/docs/doc-management' ;
2532import { fallbackLng } from '@/i18n/config' ;
33+ import { safeLocalStorage } from '@/utils/storages' ;
2634
2735import { exportCorsResolveFileUrl } from '../api/exportResolveFileUrl' ;
2836import { docxDocsSchemaMappings } from '../mappingDocx' ;
@@ -57,6 +65,16 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
5765 const [ format , setFormat ] = useState < DocDownloadFormat > (
5866 DocDownloadFormat . PDF ,
5967 ) ;
68+ const documentHasH1 =
69+ editor ?. document . some (
70+ ( block ) => block . type === 'heading' && block . props . level === 1 ,
71+ ) ?? false ;
72+
73+ const [ withTitle , setWithTitle ] = useState ( ( ) => {
74+ const stored = safeLocalStorage . getItem ( `export-with-title-${ doc . id } ` ) ;
75+ if ( stored === null ) return ! documentHasH1 ;
76+ return stored !== 'false' ;
77+ } ) ;
6078 const { untitledDocument } = useTrans ( ) ;
6179 const mediaUrl = useMediaUrl ( ) ;
6280
@@ -84,7 +102,27 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
84102
85103 const documentTitle = doc . title || untitledDocument ;
86104
87- const exportDocument = editor . document ;
105+ const titleBlock : SpecificBlock <
106+ DocsBlockSchema ,
107+ 'heading' ,
108+ DocsInlineContentSchema ,
109+ DocsStyleSchema
110+ > = {
111+ id : crypto . randomUUID ( ) ,
112+ type : 'heading' ,
113+ props : {
114+ level : 1 ,
115+ textColor : 'default' ,
116+ backgroundColor : 'default' ,
117+ textAlignment : 'left' ,
118+ } ,
119+ content : [ { type : 'text' , text : documentTitle , styles : { } } ] ,
120+ children : [ ] ,
121+ } ;
122+
123+ const exportDocument = withTitle
124+ ? [ titleBlock , ...editor . document ]
125+ : editor . document ;
88126 let blobExport : Blob ;
89127 if ( format === DocDownloadFormat . PDF ) {
90128 const exporter = new PDFExporter ( editor . schema , pdfDocsSchemaMappings , {
@@ -135,15 +173,15 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
135173 blobExport = await exporter . toODTDocument ( exportDocument ) ;
136174 } else if ( format === DocDownloadFormat . HTML ) {
137175 // Use BlockNote "full HTML" export so that we stay closer to the editor rendering.
138- const fullHtml = await editor . blocksToFullHTML ( ) ;
176+ const fullHtml = await editor . blocksToFullHTML ( exportDocument ) ;
139177
140178 // Parse HTML and fetch media so that we can package a fully offline HTML document in a ZIP.
141179 const domParser = new DOMParser ( ) ;
142180 const parsedDocument = domParser . parseFromString ( fullHtml , 'text/html' ) ;
143181
144182 const zip = new JSZip ( ) ;
145183
146- improveHtmlAccessibility ( parsedDocument , documentTitle ) ;
184+ improveHtmlAccessibility ( parsedDocument ) ;
147185 await addMediaFilesToZip ( parsedDocument , zip , mediaUrl ) ;
148186
149187 const lang = i18next . language || fallbackLng ;
@@ -273,6 +311,20 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
273311 }
274312 />
275313
314+ { format !== DocDownloadFormat . PRINT && (
315+ < Checkbox
316+ label = { t ( 'Include document title' ) }
317+ checked = { withTitle }
318+ onChange = { ( e ) => {
319+ setWithTitle ( e . target . checked ) ;
320+ safeLocalStorage . setItem (
321+ `export-with-title-${ doc . id } ` ,
322+ String ( e . target . checked ) ,
323+ ) ;
324+ } }
325+ />
326+ ) }
327+
276328 { isExporting && (
277329 < Box
278330 $align = "center"
0 commit comments