Hi everyone! I have a schema
doc: { content: 'block+' },
paragraph: {
content: 'inline*',
group: 'block',
parseDOM: [{ tag: 'p' }],
toDOM() {
return ['p', 0];
},
},
blockquote: {
content: 'paragraph+',
group: 'block',
defining: true,
parseDOM: [{ tag: 'blockquote' }],
toDOM() {
return blockquoteDOM;
},
} as NodeSpec,
code_block: {
content: 'text*',
marks: '',
group: 'block',
code: true,
defining: true,
parseDOM: [{ tag: 'pre', preserveWhitespace: 'full' }],
toDOM() {
return preDOM;
},
} as NodeSpec,
text: { group: 'inline' },
},
I have to implement blockquote and code_block formats. In a basic prosemirror example, if we select the part of any paragraph and click on code_block or blockquote, then the whole block is selected. It happens because under the hood, this example uses wrapIn command inside which we can see that code
let {$from, $to} = state.selection
let range = $from.blockRange($to) - range here is blocks around selection
In my case I need a bit different behaviour. I have to wrap in a block format the exact selection.
<p>some text</p> // select "text" and apply blockquote format
->
<p>some</p><blockquote><p>text</p></blockquote>
the same idea should work in a case if I try to apply the code_block on the part of the blockquote block
<p>some</p>
<blockquote><p>text</p></blockquote> // select "xt" and apply the code_block format
->
<p>some</p>
<blockquote><p>te</p></blockquote>
<pre><code>xt</code></pre>
I tried to implement this using different strategies. The first one is
const node = state.schema.nodes[nodeType.name].create(null, state.selection.content().content);
tr.replaceSelectionWith(node)
.setSelection(TextSelection.create(tr.doc, tr.selection.to - 2))
.scrollIntoView();
dispatch(tr);
But there is possibility that schema would be broken. The second one is
if (selection.$from.parentOffset > 0) {
tr.split(tr.mapping.map(selection.from));
}
if (selection.$to.parentOffset < selection.$to.node().content.size) {
tr.split(tr.mapping.map(selection.to));
}
tr.setSelection(
TextSelection.create(tr.doc, tr.selection.$to.pos - tr.selection.$to.depth - 1)
);
dispatch(tr);
wrapIn(nodeType)(state, dispatch, view);
In the first, I split selection by paragraphs and then use wrapIn. But, unfortunately, it works unpredictable in some cases.
I understand that my solution should have a few steps:
- Check selection, if there are some blocks like blockquote or code_block, I should analyse them
- During analysis I should cut inline parts of the node and close it
- Combine all of them and apply format
Am I right? Do you have any corrections or ideas?