Is it possible to get hold of decorations that cover the range managed by a NodeView in that NodeView?

We’re working on nesting multiple RTEs within NodeViews, each with their own node (see this post for detail). The idea is that each nested RTE is effectively a transparent ‘window’ onto the node its managing. There’s some work translating state changes into and out of these nested nodes, similar to the Prosemirror footnote example, and that’s working well in our spike.

However, I’m not sure how to pass document decorations that apply to these NodeViews into their Prosemirror instances. The NodeView initialisation and update signatures include a decorations prop, but they don’t appear to include e.g. inline decorations that cover ranges that these NodeViews manage. (The docs say decorations is an array of node or inline decorations that are active around the node.)

See this example to demonstrate what I’m seeing – the NodeView doesn’t receive the decoration for the range that it covers in its constructor and update methods, although that decoration is present in the content and the DOM.

This seems similar to this post. It may be we share a misunderstanding! I have a few questions:

  • what decorations can we expect in NodeView init and update? I thought this would include every decoration that touched the range covered by the NodeView, but that doesn’t appear to be the case.
  • if the decorations provided in init and update aren’t designed for this use, is there another way of getting hold of those decorations to pass them through? (it feels overkill to create a new DecorationSet w/ view props and find only those that touch the NodeView’s range)

Thanks for any help! If it proves successful, we’ll be open-sourcing our work here – it’s just a spike for now, and missing a license.

2 Likes

The decorations passed to the node view are only the outer decorations (the node decorations associated with the node itself). So far, I hadn’t ran into a use case where a node view would need to handle inner decorations (decorations applied to the node’s content). But those are available to the caller of the node view’s update method.

Passing them through would be a matter of adding , innerDeco to the call to this.spec.update in CustomNodeViewDesc. Would that solve your problem here?

I think so – very happy to patch locally, and if successful submit a PR w/ tests!

Does this use case make sense? Are you happy for the API to be expanded in this way? (It does look like the relevant state is already there – I’m surprised others haven’t run into this whilst managing nested RTEs! Maybe it’s just … not that common a use case :sweat_smile:)

Yes, if this is helpful in this situation I’m happy to add that to the core—it’s trivial enough to implement and doesn’t seem to introduce any potentially problematic new coupling.

Brilliant. Yes, that seems to be working as expected! I’d love to submit a patch – I’m guessing an array of decorations is what we’d expect in the constructor and update()?

Update in CustomNodeViewDesc seems to receive either a DecorationSet() or a DecorationGroup() – easy to call .find() on the DecorationSet, but unsure of the best way to extract an array of decorations from a DecorationGroup (my local patch naively maps across its members and flattens the result of find()) – what would be the idiomatic thing to do here?

I assume you’d just pass the entire decoration set (DecorationGroup is mostly compatible with that) through to the node view. That can then be provided as decorations to the inner view and (I believe) thinks should just work without expensive format conversions in between.

DecorationGroup isn’t a public interface, as far as I can see – and it doesn’t duck-type to a DecorationSet, so consumers will have to care about the difference. So as I understand it now, we’ll either have to do the conversion before we pass the decos (expensive if the consumer doesn’t care, which at the moment is everyone!), or supply the decos as a union that includes a new public interface, DecorationGroup.

Neither seem great! As a third option, we could defer the computation by passing a function rather than a value, getDecos(): Decoration[], that allows nodes that care to compute their decorations as needed?

Apologies, I’m not too familiar with the internals – perhaps there’s a more elegant solution!

Hm, I’ve just re-tested this and can no longer see DecorationGroups in the output (possibly I was passing the wrong thing by mistake?). If I haven’t made another mistake, this simplifies this task – PR is here, and thanks for the help!

Sorry, I wanted to get back to you on this earlier, but got sidetracked. You get a DecorationGroup when there’s multiple decoration sets affecting the content of a given node. So you’re right, we do need to expose a new type. I’ve added DecorationSource in this patch, and replaced DecorationSet by it in the relevant places. These changes are released as prosemirror-view 1.18.0.

1 Like