Originally I figured always rendering into a portal would be the easiest way to avoid having to deal with plugin render contention. But I'm just realizing that treating it as a HOC stack could work too, so we might end up with:
plugin.render
plugin.renderPortal
And the core plugin can actually just be the final occurrence of plugin.render to render the actual editor logic itself!
@ianstormtaylor I was curious about what will plugin.render and plugin.renderPortal be able to do and not do? Will they be helpful towards things like rendering inside void blocks or more for tooltips or hovering menus? Or an entirely different use-case?
Hey @oyeanuj, good question!
The current plugin.render (which I want to rename to plugin.renderPortal) is mostly used for things like tooltips and hovering menus, since it can only render things into a portal that isn't connected to the editor or any nodes in the DOM. However, I've personally found this super useful for allowing plugins to completely contain their own rendering logic. Previously I was rendering the hovering menu alongside the <Editor> itself, but with renderPortal plugins can actually control their own menu rendering, so there's less abstraction leaking. (It comes at a cost though for complexity inside the plugin itself.)
And then the new plugin.render would be for rendering things around the editor, or wrapping the editor. I haven't used it yet myself, since it's not implemented yet, but it would be for anything that needs to "decorate" the editor with extra styles, or elements, or whatever. It could be about rendering a static toolbar on top of it from inside a plugin, etc.
Basically both just will allow plugins to provide rendering functionality, in addition to just behavioral and schema functionality.
@ianstormtaylor Thank you for the detailed response. Question around the plugin.render method today:
The current plugin.render (which I want to rename to plugin.renderPortal) is mostly used for things like tooltips and hovering menus
I didn’t notice a render method for a plugin mentioned anywhere in the docs. Did you mean the render method that comes with the schema rules in a plugin?
You mentioned that you are using it to render portals for hovering menu, etc (as opposed to the initial approach shown in the examples). Do you have an example snippet for that?
I've personally had a harder time getting the pattern right around like, say, editable tooltips on links using the hovering menu as a concept. So, seeing an alternative approach would be helpful!
As for the new plugin.render method: I could totally see it being useful for Toolbar HoC. It seems nicer than rendering the toolbar as a sibling component, as the pattern and information flow is clearer.
Do you think another use-case would could be grammar/spell-check cases where you want to decorate the editor state with marks, before that state is rendered by the editor?
PS. (Apologies for x-posting from Slack channel. Figured more people might want to participate!)
Hey @oyeanuj, no worries.
I've just released 0.18 which adds the real plugin.render and plugin.renderPortal. (0.17 only has the portal-rendering variant, but named render.)
The portal rendering variant is how I'm handling the hovering-link-editing menu case. The menu lets you edit the link, or remove it, or just generally see where it goes. Specifically the "plugins" for it would look like:
const schema = {
schema: {
nodes: {
[LINK_INLINE]: LinkInline,
},
}
}
const inspectMenu = {
renderPortal(state, editor) {
return <LinksInspectMenu state={state} editor={editor} />
}
}
And then check out my answer to https://github.com/ianstormtaylor/slate/issues/644 for the rest of the example code.
As for spellcheck, not sure. I think that might need to be a decorator instead, since you wouldn't want to actually add those nodes to the real state I think.
Fixed in 0.18!
@ianstormtaylor Sweet, I can see this being super helpful! Just one quick question - It seems in #644 example, in LinksInspectMenu you are using SlateNodePortal which internally renders a Portal from React-Portal.
And then in the renderPortal function for the plugin model, the same seems to be happening here.
So, is the SlateNodePortal gist compatible with renderPortal? And/or does the plugin need to create its own portal? If not, how do we manage other properties that we might want to pass to the portal like positioning etc?
@oyeanuj hmm good point. Maybe the renderPortal is superfluous and we should just have the render only, since it allows people to render a portal anyways... I like that.
@ianstormtaylor actually, maybe another way to go could be to incorporate the logic that you have in SlateNodePortal in the renderPortal line in editor.js. It has a lot of logic around positioning around a passed node, controlling show/hide logic that is going to be repeated by everyone (and might actually be non-trivial for others).
Maybe it could be a plugin by itself, but I am not sure if there are many cases where one would not need the logic that you have in SlateNodePortal.
So, then render would be used strictly for wrappers or HoC cases, where renderPortal would be for menus etc, as you originally intended.
Thoughts?
I think there are cases where you don't want SlateNodePortal, and you might want a SelectionPortal instead, or just a portal that puts things at the top-left/right of the editor. I've got both of those cases currently in my setup.
I'd rather just have less, because any of those solutions can be solved by a single plugin.render handler, since then you have full control over where to put the portal.
that makes sense, I'll use just render then to render portals! thanks for all the clarifications!