@christianheine Nice! It looks neat. I have dabbled myself with integrating React to PM nodeviews which is quite the same as this. Recently I have had some trouble with figuring out how to inject the state to the nodeview components: should I use separate stores? Mobx or Redux? Only plugin state? If so, how to slice it to the components?
In the end I came to the same conclusion as you, that the state should reside inside the plugin-states and manipulated using Redux-type action/dispatcher type messaging to not to fight against the internal PM logic (and to avoid duplicating the state). However, it’s a bit problematic to then serve the plugin states to the components as you can’t just use the regular connect(mapStateToProps, mapDispatchToProps, Component)
Redux approach. I got stuck trying to customize this HOC with an efficient way of doing something along the lines of:
const pluginKey = pluginStateSlice[key]
pluginStateSlice.getState(editorView.state)
Didn’t even get to the point of thinking about debouncing the updates to the components which you have apparently added. I thought about using a plugin which at the start of every plugin transactions adds a meta-flag to the transactions eg tr.setMeta('_update', 'UPDATE')
which every transactions that happens in response to this transactions delays with eg tr.setMeta('_update', 'DELAY')
. When the value goes unmodified till the end, another plugin then adds a final meta-flag eg tr.setMeta('_update', 'FLUSH')
that flushes the changes to the React components. It’s a bit iffy still but that was one approach I thought of.
It would be easy of course if you can assume that all your transactions were independent, thus no such hacks would be needed. But since I’m not sure of the way how the updating of the React-based nodeViews (or the plugin-state in general) happens, a similar user-defined flushing would be the only way to ensure that updates were to happen only once. This way if you by some reason had a plugin that appended something to other plugin’s transaction and those plugin’s state were showed in same React nodeView, there would be no intermediate updates between them. But yep, this is for sure boilerplatish as hell.
Anyway, it’s nice to see something else figuring out the same problems as I have =). Yet I have to mention that your example doesn’t really solve the main issues with creating a true PM-Svelte integrated editor. Mainly what you need is a robust API for describing the custom PM-Svelte plugins with the boilerplate to combine the plugins with the state, event-dispatcher and whatnot. So far I have gathered those custom plugins should at least include:
-
nodeview(s)
-
schema (or more specifically SchemaSpec as Schema is already instantiated and it can’t be combined with other Schemas from other plugins)
-
PM plugins (eg keymaps & the plugin itself)
-
possible styles/features to PM as a whole eg a gutter with line-numbers or a toolbar icon
If you have taken a look into Atlassian’s atlaskit you might get the idea what I mean. It is a bit complicated but well, what can you do. TipTap would be a lot simpler and probably easier to use, but if you want to leverage as much as PM API as possible without inventing too many weird abstractions, I guess this is a valid approach.
I myself did the switch from Slate to PM partly because I felt its API was kinda bad (at least now they have removed ImmutableJS) and for what I wanted to do, which is quite low-level manipulation, it simply couldn’t do it fast enough. And I’m quite interested in Svelte as well, but yeah there’s enough stuff on my plate with this React-PM integration already
.