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 269cc81c9..e886ae9c2 100644 --- a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx +++ b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx @@ -170,7 +170,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 94bb8d8c9..3fc6a46cb 100644 --- a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx +++ b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx @@ -166,7 +166,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/core/src/exporter/Exporter.ts b/packages/core/src/exporter/Exporter.ts index b2a863093..3c4ff3f12 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 3144dbb24..20c753a2a 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 ccd1ff214..26d9d04d9 100644 --- a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml +++ b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml @@ -241,11 +241,47 @@ + + + + + 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 1b457f3c3..a73070dbd 100644 --- a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts +++ b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts @@ -77,13 +77,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 7b0c8cc08..1336a54ac 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 { @@ -178,27 +201,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) => ({ @@ -264,12 +289,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, }, ], diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx index 74a79c182..2f93548f5 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx @@ -379,16 +379,79 @@ textAlign: 'left' }} > - + - Numbered List Item + Numbered List Item starting at 10 + + + + + Numbered List Item continuing from 10 + + + + + + + + + + + Numbered List Item Nested 1 + + + + + + + + + + + Numbered List Item Nested 2 + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - Numbered List Item + Numbered List Item starting at 10 + + + + + Numbered List Item continuing from 10 + + + + + + + + + + + Numbered List Item Nested 1 + + + + + + + + + + + Numbered List Item Nested 2 + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - +