I’m not a big fan of the default list behaviour of wrapInList from prosemirror-schema-list. I want the button to act as a toggle. So if you click the bullet list button in a bullet list, it removes the list, if you click a bullet list button in a numbered list, it swaps the numbered list for a bullet list. If you click it outside of any list, it just wraps in a bullet list.
So far, I have:
function isList(node: Node) {
return (
node.type === spec.nodes.bullet_list ||
node.type === spec.nodes.ordered_list
);
}
function toggleList(listType: NodeType) {
const lift = liftListItem(spec.nodes.list_item);
const wrap = wrapInList(listType);
return (
state: EditorState,
dispatch?: (tr: Transaction) => void,
): boolean => {
const {$from, $to} = state.selection;
const range = $from.blockRange($to);
if (!range) {
return false;
}
if (range.depth >= 2 && $from.node(range.depth - 1).type === listType) {
return lift(state, dispatch);
} else if (range.depth >= 2 && isList($from.node(range.depth - 1))) {
const tr = state.tr;
// const node = $from.node(range.depth - 1);
// TODO: how do I pass the node above to `setNodeType`?
// tr.setNodeType(range.start, listType);
if (dispatch) dispatch(tr);
return false;
} else {
return wrap(state, dispatch);
}
};
}
But I’m stuck on how to resolve that TODO item. This is my first time trying to write my own transaction code, so any help is really appreciated.



So I’ve broken down and come looking for guidance. I would share what I’ve tried, but honestly none of it seemed to show much promise, so I’m pretty much right back at the code DigiBytes suggested.
