From d069e7dacc34714783b1137240a7c5676005102a Mon Sep 17 00:00:00 2001 From: mfanselmo Date: Tue, 7 Jan 2025 10:33:12 -0300 Subject: [PATCH 1/2] feat: Handle number list start number in export to pdf --- .../05-converting-blocks-to-pdf/App.tsx | 19 ++++++- .../06-converting-blocks-to-docx/App.tsx | 19 ++++++- .../src/pdf/__snapshots__/example.jsx | 56 ++++++++++++++++++- .../exampleWithHeaderAndFooter.jsx | 56 ++++++++++++++++++- .../xl-pdf-exporter/src/pdf/pdfExporter.tsx | 3 + shared/testDocument.ts | 19 ++++++- 6 files changed, 165 insertions(+), 7 deletions(-) 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 84b3d7b073..99dccfb211 100644 --- a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx +++ b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx @@ -157,7 +157,24 @@ export default function App() { }, { type: "numberedListItem", - content: "Numbered List Item", + content: "Numbered List Item starting at 10", + props: { + start: 10, + }, + }, + { + type: "numberedListItem", + content: "Numbered List Item continuing from 10", + children: [ + { + type: "numberedListItem", + content: "Numbered List Item Nested 1", + }, + { + type: "numberedListItem", + content: "Numbered List Item Nested 2", + }, + ], }, { type: "checkListItem", 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 3f0618546e..5a29b5b725 100644 --- a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx +++ b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx @@ -152,7 +152,24 @@ export default function App() { }, { type: "numberedListItem", - content: "Numbered List Item", + content: "Numbered List Item starting at 10", + props: { + start: 10, + }, + }, + { + type: "numberedListItem", + content: "Numbered List Item continuing from 10", + children: [ + { + type: "numberedListItem", + content: "Numbered List Item Nested 1", + }, + { + type: "numberedListItem", + content: "Numbered List Item Nested 2", + }, + ], }, { type: "checkListItem", diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx index e7158793d3..9b2225d2d1 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx @@ -261,16 +261,68 @@ - 1. + 10. - Numbered List Item + Numbered List Item starting at 10 + + + + + 11. + + + + + Numbered List Item continuing from 10 + + + + + + + + + + 1. + + + + + Numbered List Item Nested 1 + + + + + + + + + 2. + + + + + Numbered List Item Nested 2 + + + + + diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx index fc93b8c7d0..308429fd74 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx @@ -266,16 +266,68 @@ - 1. + 10. - Numbered List Item + Numbered List Item starting at 10 + + + + + 11. + + + + + Numbered List Item continuing from 10 + + + + + + + + + + 1. + + + + + Numbered List Item Nested 1 + + + + + + + + + 2. + + + + + Numbered List Item Nested 2 + + + + + diff --git a/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx b/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx index 969f1ce759..13146b7c71 100644 --- a/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx +++ b/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx @@ -135,6 +135,9 @@ export class PDFExporter< for (const b of blocks) { if (b.type === "numberedListItem") { numberedListIndex++; + if (b.props.start !== undefined) { + numberedListIndex = b.props.start as number; + } } else { numberedListIndex = 0; } diff --git a/shared/testDocument.ts b/shared/testDocument.ts index 281e5f9d7b..f13f56d535 100644 --- a/shared/testDocument.ts +++ b/shared/testDocument.ts @@ -137,7 +137,24 @@ export const testDocument = partialBlocksToBlocksForTesting( }, { type: "numberedListItem", - content: "Numbered List Item", + content: "Numbered List Item starting at 10", + props: { + start: 10, + }, + }, + { + type: "numberedListItem", + content: "Numbered List Item continuing from 10", + children: [ + { + type: "numberedListItem", + content: "Numbered List Item Nested 1", + }, + { + type: "numberedListItem", + content: "Numbered List Item Nested 2", + }, + ], }, { type: "checkListItem", From 7886b2534706fd28308e7031e005996962c86709 Mon Sep 17 00:00:00 2001 From: mfanselmo Date: Wed, 8 Jan 2025 16:34:56 -0300 Subject: [PATCH 2/2] feat: add numbered list handling to export in docx --- packages/core/src/exporter/Exporter.ts | 14 +++- packages/core/src/exporter/mapping.ts | 4 +- .../src/docx/__snapshots__/basic/document.xml | 38 ++++++++++- .../src/docx/defaultSchema/blocks.ts | 14 +++- .../xl-docx-exporter/src/docx/docxExporter.ts | 66 +++++++++++++------ 5 files changed, 110 insertions(+), 26 deletions(-) diff --git a/packages/core/src/exporter/Exporter.ts b/packages/core/src/exporter/Exporter.ts index b2a863093e..3c4ff3f124 100644 --- a/packages/core/src/exporter/Exporter.ts +++ b/packages/core/src/exporter/Exporter.ts @@ -44,6 +44,8 @@ export abstract class Exporter< RS, TS > { + public numberingSectionStarts: Set = new Set(); + public constructor( _schema: BlockNoteSchema, // only used for type inference protected readonly mappings: { @@ -86,16 +88,24 @@ export abstract class Exporter< public abstract transformStyledText(styledText: StyledText): TS; + public addNumberingSectionStart(number: number) { + this.numberingSectionStarts.add(number); + } + public async mapBlock( block: BlockFromConfig, nestingLevel: number, - numberedListIndex: number + numberedListIndex?: number, + numberedListStart?: number, + numberedListIntance?: number ) { return this.mappings.blockMapping[block.type]( block, this, nestingLevel, - numberedListIndex + numberedListIndex, + numberedListStart, + numberedListIntance ); } } diff --git a/packages/core/src/exporter/mapping.ts b/packages/core/src/exporter/mapping.ts index 3144dbb24d..20c753a2a6 100644 --- a/packages/core/src/exporter/mapping.ts +++ b/packages/core/src/exporter/mapping.ts @@ -26,7 +26,9 @@ export type BlockMapping< // this is why there are many `any` types here (same for types below) exporter: Exporter, nestingLevel: number, - numberedListIndex?: number + numberedListIndex?: number, + numberedListStart?: number, + numberedListIntance?: number ) => RB | Promise; }; 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 252040fe31..b26705131d 100644 --- a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml +++ b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml @@ -245,11 +245,47 @@ Line 2 + + + + + Numbered List Item starting at 10 + + + + + + + + + + + + Numbered List Item continuing from 10 + + + + + + + + + + + + Numbered List Item Nested 1 + + + + + + + - Numbered List Item + Numbered List Item Nested 2 diff --git a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts index 43bfba9781..9933e0bd7f 100644 --- a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts +++ b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts @@ -74,13 +74,23 @@ export const docxBlockMappingForDefaultSchema: BlockMapping< }, }); }, - numberedListItem: (block, exporter, nestingLevel) => { + numberedListItem: ( + block, + exporter, + nestingLevel, + _numberedListIndex, + numberedListStart, + numberedListInstance + ) => { + exporter.addNumberingSectionStart(numberedListStart!); + return new Paragraph({ ...blockPropsToStyles(block.props, exporter.options.colors), children: exporter.transformInlineContent(block.content), numbering: { - reference: "blocknote-numbered-list", + reference: `blocknote-numbered-list-${numberedListStart}`, level: nestingLevel, + instance: numberedListInstance, }, }); }, diff --git a/packages/xl-docx-exporter/src/docx/docxExporter.ts b/packages/xl-docx-exporter/src/docx/docxExporter.ts index 2be6106591..1f0ff2aba2 100644 --- a/packages/xl-docx-exporter/src/docx/docxExporter.ts +++ b/packages/xl-docx-exporter/src/docx/docxExporter.ts @@ -111,8 +111,25 @@ export class DOCXExporter< nestingLevel = 0 ): Promise> { const ret: Array = []; + let currentNumberingStart = 1; + let numberingInstanceCount = 0; + let isFirstNumberedListItem = false; for (const b of blocks) { + if (b.type === "numberedListItem") { + if (!isFirstNumberedListItem) { + currentNumberingStart = 1; + numberingInstanceCount += 1; + isFirstNumberedListItem = true; + } + if (b.props.start !== undefined) { + currentNumberingStart = b.props.start as number; + numberingInstanceCount += 1; + } + } else { + isFirstNumberedListItem = false; + } + let children = await this.transformBlocks(b.children, nestingLevel + 1); children = children.map((c, _i) => { // NOTE: nested tables not supported (we can't insert the new Tab before a table) @@ -128,7 +145,13 @@ export class DOCXExporter< } return c; }); - const self = await this.mapBlock(b as any, nestingLevel, 0 /*unused*/); // TODO: any + const self = await this.mapBlock( + b as any, + nestingLevel, + 0 /* unused */, + currentNumberingStart, + numberingInstanceCount + ); // TODO: any if (Array.isArray(self)) { ret.push(...self, ...children); } else { @@ -160,27 +183,29 @@ export class DOCXExporter< .default; const bullets = ["•"]; //, "◦", "▪"]; (these don't look great, just use solid bullet for now) + const generateNumberingConfig = (start: number) => ({ + reference: `blocknote-numbered-list-${start}`, + levels: Array.from({ length: 9 }, (_, i) => ({ + start, + level: i, + format: LevelFormat.DECIMAL, + text: `%${i + 1}.`, + alignment: AlignmentType.LEFT, + style: { + paragraph: { + indent: { + left: DEFAULT_TAB_STOP * (i + 1), + hanging: DEFAULT_TAB_STOP, + }, + }, + }, + })), + }); + return { numbering: { config: [ - { - reference: "blocknote-numbered-list", - levels: Array.from({ length: 9 }, (_, i) => ({ - start: 1, - level: i, - format: LevelFormat.DECIMAL, - text: `%${i + 1}.`, - alignment: AlignmentType.LEFT, - style: { - paragraph: { - indent: { - left: DEFAULT_TAB_STOP * (i + 1), - hanging: DEFAULT_TAB_STOP, - }, - }, - }, - })), - }, + ...[...this.numberingSectionStarts].map(generateNumberingConfig), { reference: "blocknote-bullet-list", levels: Array.from({ length: 9 }, (_, i) => ({ @@ -246,12 +271,13 @@ export class DOCXExporter< documentOptions: {}, } ) { + const transformedBlocks = await this.transformBlocks(blocks); const doc = new Document({ ...(await this.createDefaultDocumentOptions()), ...options.documentOptions, sections: [ { - children: await this.transformBlocks(blocks), + children: transformedBlocks, ...options.sectionOptions, }, ],