Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for numbered list item when exporting to PDF, DOCX #1357

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/exporter/Exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export abstract class Exporter<
RS,
TS
> {
public numberingSectionStarts: Set<number> = new Set();

public constructor(
_schema: BlockNoteSchema<B, I, S>, // only used for type inference
protected readonly mappings: {
Expand Down Expand Up @@ -86,16 +88,24 @@ export abstract class Exporter<

public abstract transformStyledText(styledText: StyledText<S>): TS;

public addNumberingSectionStart(number: number) {
this.numberingSectionStarts.add(number);
}

public async mapBlock(
block: BlockFromConfig<B[keyof B], I, S>,
nestingLevel: number,
numberedListIndex: number
numberedListIndex?: number,
numberedListStart?: number,
numberedListIntance?: number
) {
return this.mappings.blockMapping[block.type](
block,
this,
nestingLevel,
numberedListIndex
numberedListIndex,
numberedListStart,
numberedListIntance
);
}
}
4 changes: 3 additions & 1 deletion packages/core/src/exporter/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export type BlockMapping<
// this is why there are many `any` types here (same for types below)
exporter: Exporter<any, any, any, RB, RI, any, any>,
nestingLevel: number,
numberedListIndex?: number
numberedListIndex?: number,
numberedListStart?: number,
numberedListIntance?: number
) => RB | Promise<RB>;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,47 @@ Line 2</w:t>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="4"/>
</w:numPr>
</w:pPr>
<w:r>
<w:t xml:space="preserve">Numbered List Item starting at 10</w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="0"/>
<w:numId w:val="4"/>
</w:numPr>
</w:pPr>
<w:r>
<w:t xml:space="preserve">Numbered List Item continuing from 10</w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="1"/>
<w:numId w:val="3"/>
</w:numPr>
</w:pPr>
<w:r>
<w:t xml:space="preserve">Numbered List Item Nested 1</w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="1"/>
<w:numId w:val="3"/>
</w:numPr>
</w:pPr>
<w:r>
<w:t xml:space="preserve">Numbered List Item</w:t>
<w:t xml:space="preserve">Numbered List Item Nested 2</w:t>
</w:r>
</w:p>
<w:p>
Expand Down
14 changes: 12 additions & 2 deletions packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
});
},
Expand Down
66 changes: 46 additions & 20 deletions packages/xl-docx-exporter/src/docx/docxExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,25 @@ export class DOCXExporter<
nestingLevel = 0
): Promise<Array<Paragraph | Table>> {
const ret: Array<Paragraph | Table> = [];
let currentNumberingStart = 1;
let numberingInstanceCount = 0;
let isFirstNumberedListItem = false;

for (const b of blocks) {
if (b.type === "numberedListItem") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is a bit messy and could definitely be improved, but its the best I could come up with to ensure a unique instance id is given to each numbered list. In theory this id should guarantee no weird continuations of non adjacent lists happen, but for some reason they still are. I would not merge this as is

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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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) => ({
Expand Down Expand Up @@ -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,
},
],
Expand Down
56 changes: 54 additions & 2 deletions packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,68 @@
<view>
<view>
<text>
1.
10.
</text>
</view>
<text>
<text>
Numbered List Item
Numbered List Item starting at 10
</text>
</text>
</view>
</view>
<view
style="text-align: left;"
>
<view>
<view>
<text>
11.
</text>
</view>
<text>
<text>
Numbered List Item continuing from 10
</text>
</text>
</view>
</view>
<view
style="margin-left: 18px;"
>
<view
style="text-align: left;"
>
<view>
<view>
<text>
1.
</text>
</view>
<text>
<text>
Numbered List Item Nested 1
</text>
</text>
</view>
</view>
<view
style="text-align: left;"
>
<view>
<view>
<text>
2.
</text>
</view>
<text>
<text>
Numbered List Item Nested 2
</text>
</text>
</view>
</view>
</view>
<view
style="text-align: left;"
>
Expand Down
Loading
Loading