Problems with Custom Node

Hi! I have a simple custom node defined as such:

export const RootBlock = Node.create({
  name: "rootblock",
  group: "rootblock",
  content: "block", // Ensure only one block element inside the rootblock
  draggable: true, // Make the node draggable
  selectable: true, // Node isn't selectable
  inline: false, // Node is a block-level element
  priority: 1000, // Priority for node resolution
}).  

and a custom Doc as such:

const Document = Node.create({
  name: 'doc',
  topNode: true,
  content: "rootblock+",
})

The purpose is simple. Each rootnode can only hold 1 block. I use this to add attributes, define custom functionality and allow for a custom handle to DND the block without worrying about selecting all the text below it etc.

However, I am running into a few issues:

  1. when i select text that bleeds across multiple rootblocks, the fragment from/to is always different than the view.selection from/to. i can delete without errors if the selection is made via keyboard. but if I drag select via mouse, often, i will run into a Uncaught RangeError: Position x outside of fragment (since the fragment seems to be constrained to the 1 rootblock and not across blocks). This causes problems for future adding blocks (via Enter key) and sometimes, will eventually lead to this error also: Failed to execute ‘insertBefore’ on ‘Node’: The node before which the new node is to be inserted is not a child of this node. Any thoughts how to resolve?

  2. having a rootnode above the block also affects the ability to convert text across multiple rootblocks to bullets / lists, and vice versa. also when i key down / up (if the selection starts in the middle of the text), the behavior is different than under default block settings. i wonder if there’s an easy way to configure the “rootblock wrapper” without causing all these side effects.

Alternatively, I tried embedding the attributes / functionalities with the different block types (paragraph, heading, etc) but this causes DND issues and other complications.

Any recommendation how to approach this? In general, I need custom attributes, ability to DND via a drag handle, ability to work with DOM and appendTransaction, which I’ve generally been able to do with the rootblock wrapper.

Thanks in advance!

What do you mean by ‘fragment from/to’ here?

This is how people generally do this, though. Adding a level of wrapper nodes just to add attributes is going to cause a lot of complications that you probably don’t want here.

HI marijn - thanks for the quick reply on this!

on (1) I mean the fragment range vs. selection range. by placing a rootblock, the selection can span multiple nodes but fragment seems to only be constrained to one - it creates weird side effects that i didn’t anticipate.

let me try the latter as you suggested. this makes sense and seems more elegant to me.

I’m curious though, do you consider having a common nodeView across multiple block types to be good practice? I’m trying to have some common visual components (similar to a notion-like side menu) and curious what you think on this as well.

cheers!

You mean fragment as in the Fragment class? That represents a flat sequence of nodes. I’m not sure how/why you’d be trying to represent a selection with it.

It’s definitely a thing you can do. You just have to be careful to write its update method correctly.

thanks marijn. on (1) yes - from Fragment class. actually, no selection made on my part. just an observation that a node wrapper seems to cause some nasty side effects, triggered by differences between DOM selection and the range represented in Fragment.

The library doesn’t ever represent a selection as a fragment. Whatever you’re doing there (and I’m still clueless on what it might be) isn’t how this is supposed to be used.

ok thanks. i’m using ProseMirror via the Tiptap implementation. I think after more digging, i think it’s an issue with how they have NodeViewWrapper and NodeViewContent implemented. even when i reworked the node structure with base blocks, i can always drag select an edge and delete and cause a out-of-range error to trigger, so long as i try to delete multiple blocks at the same time.