How to wrap the nodeView's node in a block upon an event


I have a case where I would want to wrap the nodeView’s node (called here paragraphNode) in another block node (called containerNode) upon an ondrop event when something is dropped on the paragraphNode.

So within the ondrop handler that is wired up within the nodeView class I would like to get hold of the paragraphNode and in case it is not already wrapped in containerNode I would like to create a containerNode to wrap the paragraphNode. Regarding this I’d have two questions:

  1. How can I wrap the paragraphNode in a containerNode with the information available in the nodeView context (node, view, getPos, decorations) ?
  2. How could I best check if the paragraphNode is already contained by some other node, in this case containerNode?

Regarding the question 1, I have tried the following approach:

const startPosition = node.resolve(getPos()); const endPosition = node.resolve(getPos() + node.nodeSize); const range = new NodeRange(startPosition, endPosition, 0); const { tr } = view.state; tr.wrap(range, [{ type: documentSchema.nodes.containerBlock }]); view.dispatch(tr);

With this I get the error ‘RangeError: Position 52 out of range’ where 52 of course varies based on the content length.

If I modify to endPosition calculation to

const endPosition = node.resolve(getPos() + node.nodeSize - 2);

The resulting error changes to ‘TransformError: Gap is not a flat range’

I think the issue is that you’re resolving document-wide positions inside your local node. You’ll have to store a reference to the editor view in your node view, so that it can get the current state and document from there.

Thank you very much for the help.

As you advised, the key was to use view.doc.resolve to get the resolved position.

I got this working and here are the key points how this worked out in case anyone is interested in future (I do not know if this is the most elegant or robust way of doing things but so far works without issues):

  1. To get the resolved position:

const resolvedPosition = view.state.doc.resolve(getPos())

  1. To check if the paragraphNode is already contained by containerNode:

const parent = resolvedPosition.parent if (parent.hasMarkup(documentSchema.nodes.containerBlock)) { // paragraphNode is contained by containerNode }

  1. To wrap the paragraphNode with containerNode:

const { tr } = view.state; const endPosition = view.state.doc.resolve(getPos() + node.nodeSize); const range = new NodeRange(resolvedPosition, endPosition, 0); tr.wrap(range, [{ type: documentSchema.nodes.containerBlock, attrs: { containerId: ‘somevalue’ } }]); view.dispatch(tr);

Thanks again!