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

Issue with nested list (numbered and bullets mixed) #23

Open
kozborn opened this issue May 21, 2024 · 0 comments
Open

Issue with nested list (numbered and bullets mixed) #23

kozborn opened this issue May 21, 2024 · 0 comments

Comments

@kozborn
Copy link

kozborn commented May 21, 2024

There is a problem when types of list are mixed together e.g bullets list is being nested in numbered list or other way around.

image

results with

image

I was able to fix it in our code base, I'll try to prepare PR for this so you could take a look.

Btw, great job with this library, it's really helpfull

Ok, I can't push branch to do a PR so here's my code that I've changed, maybe it'll help someone

numbering.ts -- full file

import { AlignmentType, convertInchesToTwip, ILevelsOptions, LevelFormat } from 'docx';
import { INumbering } from './types';

function basicIndentStyle(indent: number): Pick<ILevelsOptions, 'style' | 'alignment'> {
  return {
    alignment: AlignmentType.START,
    style: {
      paragraph: {
        indent: { left: convertInchesToTwip(indent), hanging: convertInchesToTwip(0.18) },
      },
    },
  };
}

const numbered = (indentStart: number = 0) =>
  Array(3)
    .fill([LevelFormat.DECIMAL, LevelFormat.LOWER_LETTER, LevelFormat.LOWER_ROMAN])
    .flat()
    .map((format, level) => ({
      level,
      format,
      text: `%${level + 1}.`,
      ...basicIndentStyle(indentStart / 2),
    }));

const bullets = (indentStart: number = 0) =>
  Array(3)
    .fill(['●', '○', '■'])
    .flat()
    .map((text, level) => ({
      level,
      format: LevelFormat.BULLET,
      text,
      ...basicIndentStyle(indentStart / 2),
    }));

const styles = {
  numbered,
  bullets,
};

export type NumberingStyles = keyof typeof styles;

export function createNumbering(
  reference: string,
  style: NumberingStyles,
  indentStart: number,
): INumbering {
  return {
    reference,
    levels: styles[style](indentStart),
  };
}

and in serializer.ts

type NumberingStackItem = {
  reference: string;
  level: number;
  style: NumberingStyles;
};

...

export class DocxSerializerState {

...

currentListIndent: number = 0;
numberingStack: NumberingStackItem[] = [];

...

renderList(node: PMModel.Node, style: NumberingStyles) {
    const nextId = createShortId();

    this.numbering.push(createNumbering(nextId, style, this.currentListIndent));
    const levels: number[] = this.numberingStack
      .filter((n) => n.style === style)
      .map((n) => n.level);

    const level = Math.max(...(levels.length !== 0 ? levels : [-1])) + 1;

    this.currentListIndent += 1;
    this.numberingStack.push({ reference: nextId, level, style });

    this.renderContent(node);
    this.numberingStack.pop();
    this.currentListIndent -= 1;
  }

  // This is a pass through to the paragraphs, etc. underneath they will close the block
  renderListItem(node: PMModel.Node) {
    if (this.numberingStack.length === 0)
      throw new Error('Trying to create a list item without a list?');

    this.addParagraphOptions({
      numbering: this.numberingStack[this.numberingStack.length - 1],
    });
    this.renderContent(node);
  }

...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant