Separating out document state from view 🤔

Hey, I am exploring the idea of separating out data model (EditorState) from how its being rendered (view), specifically we want to be able to sort/filter table rows without changing the content of the table in the document, so that filter/sort state would be controlled only by an attribute (e.g. attrs: {sort: ‘a-z’}).

Since each node only knows how to render itself (and specify the content “hole”), it seems like there’s no way to influence child nodes from the parent node (e.g. filter out/sort rows from table node), or at least I couldn’t figure out one :slight_smile:

Would it make sense to extend the NodeView API to possibly provide a function that would operate on children, something like:

preRender: (indexes: number[]) => number[], where indexes would be an array of indexes of child nodes, so that we could return what child nodes need to be rendered and in what order. ProseMirror document stays the same, positions of nodes would be the same, the only difference would be in how a node’s content is rendered.

What would be the implications of such approach? I am happy to go with whatever is best for the community and I’m open to considering alternative approaches. Cheers.

The assumption that DOM order corresponds to document order is present all over the view code. So this would be hugely invasive to do.

What you could do, I guess, is create a separate sub-editor for each row, and make the table a node view without a contentDOM property.

Can you elaborate on sub-editor part? Are you suggesting treating each row as a separate sub-document by creating an EditorState and EditorView for each row?

Yes. That requires a bunch of plumbing, but should work (and isn’t as expensive as it sounds—individual ProseMirror editors aren’t much more expensive than the DOM elements they contain).

(You will have some side effects, though, such as no longer being able to select across rows.)

Yeah, performance is definitely something we really care about, I would benchmark that solution before making any assumptions, although it seems it’d add a certain overhead.

I have a few other concerns around that:

  • any transaction dispatched inside a row would be performed on a local state of that row, non of the plugins would know anything about it, which makes it complicated to communicate information between plugins (e.g. tracking analytics in one place, processing transactions in collab plugin, validation of transaction steps etc.)
  • most of the table manipulations would have to change (e.g. adding columns would require updating state of each row separately)
  • as you mentioned, cross-row selection would no longer work
  • would “fixTables” logic and copy paste still work?
  • would TableMap still work?

I’m not sure we’re ready to maintain this complexity.

Getting back to my original proposal, what would be your estimation of the work required to make prosemirror-view compatible with this change?

The idea would be to forward transactions in inner editors to the outer editor (and vice-versa), as in the footnote example, which sidesteps many of these problems. Table-fixing could happen on the outer state, which has all the information needed.

This really seems too obscure a case to make major changes to the core library for (and I can’t really see this working without major changes).

ProseMirror’s general approach is to show as plain a view of the content that’s being edited. Sortable views on tables inside the editor are really pushing that scope.

That makes sense, it does seem like a huge effort. Thanks anyway!

Just wanted to say I am also interested in this capability. It would be really, really useful for the knowledge management app I’m developing.