Open GermanJablo opened 1 year ago
Thx for starting this discussion @EgonBolton. I am supportive for the 2 first goal (and probably also the 3rd one which I understand less).
Quick questions:
Thanks @Echarles.
Point 3 is basically about verbosity and repetition. I mean that extending a node for something as simple as adding a property requires a lot of boilerplate, unnecessary in my opinion (example above). If you also want to add that same property to different nodes, you must repeat that code, making it difficult to maintain.
How related is this proposal to the node replacement system #3367?
Both are intended to simplify the way nodes are extended. Before that solution, extending nodes was even more complicated. You would have to overwrite all the commands, plugins, or functions that created the node you wanted to replace.
If you have a POC at hand, a sandbox that demonstrates how you can convert an indented paragraph to heading (and back) while keeping the indent would be help demonstrate the value of this proposal.
In fact, it seems that they have solved just the use case of indents (I put it in the edit of problem 1). You can try it in the playground. However, this cannot be done by us with custom properties of our nodes (as requested by @aquarius-wing at https://github.com/facebook/lexical/issues/2557).
I think this would be a very large change to the current architecture. Not saying it's a bad thing, but introducing such changes now would be very churning on the existing set of nodes and plugins.
Update: This proposal can be divided into parts. The first PR that is to eliminate the Clone
method is already pending review:
Update2: ExportJSON/ImportJSON
on the way
The reason I started with Clone() is because I found a way to generalize it without even requiring _properties
as I proposed above.
The Problems
In https://github.com/facebook/lexical/issues/1262#issuecomment-1186170960 I explained a series of problems when it comes to extending nodes.
At that time I thought that the best way to solve it was to group different nodes in the same class (as is done now with the different levels of Heading). In fact later in the same thread, I offered a proof of concept. I still think this could potentially reduce code repetition in very similar nodes like
headings
andparagraphs
, but after thinking about it a lot I came to the conclusion that it is not a viable solution, at least for all the problems that I described.After that the node replacement API came out. Although it solves some problems, I think the first 3 items of my initial post are still valid:
Things I've Tried
So once I had the node replacements API available, I decided to ignore issues 1 and 2 for the moment and focus on issue 3. That is, I tried to reduce the excessive code repetition required to add a shared property to multiple nodes at once; and I did it using a combination of the node replacement API and the mixin pattern.
In the process I ran into many difficulties, but I came up with an idea that I think would solve all 3 problems at once.
New idea: how I think extending a node should look like.
As I mentioned in the other thread, note for example how applications like Notion, Dynalist, Remnote, Roam Research and Workflowy have properties such as color shared by what in Lexical we would call nodes of type element.
If a user wanted to do something like this with Lexical, for each of the nodes they would do something like this:
Instead, the solution I have in mind would reduce it to just this:
Note: I think there are ways to simplify this even further, but I'll leave it as a starting point for now.
Implementation
The way to avoid defining properties, getters and setters in each class would be with the following primitives:
Cloning or serializing a class instance is tricky. But by making the properties a simple object we could do without explicitly defining the clone(), importJSON or exportJSON() methods for every subclass.
Each class would inherit those methods something like:
Of course, an interface could be defined to the
__properties
object, to get the typesafety.The point is that the properties would be decoupled from the nodes. In other words: although
li
, orparagraph
are not going to use the "checked" property, it would still be nice if they did, which is what I explained in points 1 and 2 at the beginning of the post.Then, keeping any arbitrary properties when converting one node to another would be trivial. You would only have to clone the properties in the
$setBlocksType
functionThis could fix: