Joining multiple lists and text blocks

I am trying to implement the following user action:

  1. User selects multiple nodes:

    Text before

    • A
    • B
    • C

    Text between

    • D
    • E
    • F

    Text after

  2. User clicks the bullet button

  3. Output:

    • Text before
    • A
    • B
    • C
    • Text between
    • D
    • E
    • F
    • Text after

The closest I have come to a solution is the following:

const { $from, $to, from, to } = state.selection;

const listItems = [];
state.doc.node.nodesBetween(from, to, node => {
	//Check if the node is a paragraph or list_item
	//Grab node's fragment and generate a new list_item node
	//Add new node to listItems
	return false;
});

//Replace selection with new list node
dispatch(state.tr.replaceSelection(from, to, nodeType.create(null, listItems)).scrollIntoView());
return true;

I have the following questions:

  1. What am I not understanding about ProseMirror’s data structure?
  2. Is looping through each node in the selection the correct approach?
  3. Should I try transforming the TextSelection itself?

I think you probably want to find the outer node for the selection (see for example blockRange) and iterate through the selected nodes on a single level.

Then, you could wrap each of those that’s not already a list in a list, and finally join them all together. This does require some non-trivial code, but it’s definitely doable.