Discussion : Replacing BulletList -> ListItem with an infinitely nestable CustomListItem

Hello, I’m exploring ProseMirror with a view to checking its suitability to serve as an outliner editor.

In this context, my document is essentially one huge nestable list type (for ex BulletList). If I understand the concept of having a BulletList and ListItem correctly, a BulletList is a collection (array) of one or more ListItems, while a ListItem can have either a paragraph or any other blocks (like a heading or a quote or even another BulletList).

In my scenario the simple approach would be restrict my version of ListItem to only allow paragraph or another BulletList as content. In such a scenario, the json representation of something like

image

would be like :

  • doc, content :
  • –> bullet_list, content :
  • –> --> list_item, content :
  • –> --> --> paragraph, content : text (a)
  • –> --> --> bullet_list, content :
  • –> --> --> --> list_item, content :
  • –> --> --> --> --> paragraph, content : text (b)
  • –> --> --> --> --> bullet_list, content :
  • –> --> --> --> --> --> list_item, content :
  • –> --> --> --> --> --> --> paragraph, content : text ©

Instead, if I use a customListItem that allows as its content either paragraph or another customListItem, my representation should become

  • doc, content :
  • –> custom_list_item, content :
  • –> --> paragraph. content : text (a)
  • –> --> custom_list_item, content :
  • –> --> --> paragraph, content: text (b)
  • –> --> --> custom_list_item, content :
  • –> --> --> --> paragraph, content: text c

which is much more succinct and intuitive for my use case

My concerns are

  • what are some reasons the customListItem approach can prove problematic?
  • Are there amy existing examples of any blocks I can use as template?

Any discussions around this most welcome!

Hello!

It’s easiest to explain why the current approach is preferred if I change your example somewhat:

  • a
    • b
    • c
      • d

For the above list, the generated HTML structure is:

<ul>
  <li>
    <p>a</p>
    <ul>
      <li>
        <p>b</p>
      </li>
      <li>
        <p>c</p>
        <ul>
          <li>
            <p>d</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

Given that, the ProseMirror model would be:

doc
  bullet_list -- <ul>
    list_item -- <li>
      paragraph -- <p>a</p>
      bullet_list -- <ul>
        list_item -- <li>
          paragraph -- <p>b</p>
        list_item -- <li>
          paragraph -- <p>c</p>
          bullet_list -- <ul>
              list_item -- <li>
                paragraph -- <p>d</p>

Notably, there’s a clean, regular serialization from the ProseMirror model to the HTML model.

Doing the same experiment with your custom list schema yields:

doc
  custom_list_item -- <ul><li>
    paragraph -- <p>a</p>
    custom_list_item -- <ul><li>
      paragraph -- <p>b</p>
    custom_list_item -- <ul><li>
      paragraph -- <p>c</p>
      custom_list_item -- <ul><li>
        paragraph -- <p>d</p>

This clearly also works, but it has one notable downside: your custom_list_item only ever semantically creates lists of a single item.

Now, it may be that the semantic markup of your lists don’t matter in your use case. If you never use ordered lists, it can be made invisible to end users.

As for existing examples, I’d probably start with the NodeSpecs from prosemirror-schema-list.

1 Like

Hmmm… I think calling it a CustomListItem was the wrong nomenclature… what it’s effectively describing is a standard tree structure created using something like pointers…

The ‘paragraph’ bit is the node’s data so to say, and then we have an optional array of other child nodes…

I had not actually thought about mapping each node to a <ul><li> type dom structure… I’m using tiptap to access prosemirror from within my vue app and my initial idea was that I’d like each node to render into a different template/component based on its custom attributes (one of which would a unique id). I’m not 100% sure if it’s possible or even necessary but that would break the semantics somewhat fiercely in any case…

Now that I’m thinking from that perspective… not supporting the standard <ul><li> format would mean that copy paste of data would require additional complexity… but using that instead of custom divs would be like trying to implement a treeview using <ul> and <li> elements alone :thinking:

I think I need to read up more and test out more stuff… I definitely don’t know enough about prosemirror at this stage to even ask intelligent questions :woman_facepalming:

Thanks a lot for your response! It’s helped me understand there’s more to think about than simple schema structure.

adding Suitable backspace command for lists with restricted schema? for context.

I decided to try the bullet_list list_item approach first and only try the other one if I run into dealbreakers.