Problem
Plugins and nodes are tightly coupled and it makes it harder to extend default nodes and maintaining existing plugins functionality.
Example
Let's take markdown plugin (copy/paste logic is also a good example), it exports bunch of node creation helpers ($createHeadingNode, $createCodeBlockNode, $createListNode, etc).
Then if I want to extend HeadingNode behaviour, for example to add id attribute to each heading so it can be used as an anchor (smth like <h1 id="getting-started-with-react">Getting started with React</h1>, see our README doing it). To do so I'd extend default node and add new behaviour on top:
class AnchorHeadingNode extends HeadingNode {
static getType() {
return 'heading';
}
createDOM<EditorContext>(config: EditorConfig<EditorContext>): HTMLElement {
const element = super.createDOM(config);
element.setAttribute('id', getAnchorID(this));
return element;
}
updateDOM(prevNode: HeadingNode, dom: HTMLElement): boolean {
dom.setAttribute('id', getAnchorID(this));
return false;
}
}
But now the problem is that all plugins that might insert HeadingNode (markdown, toolbar, copy/paste), they all keep using default HeadingNode, because they all use $createHeadingNode imported from default heading node file.
Potential solution:
Replace $createHeadingNode() implementation from
function $createHeadingNode(tag): HeadingNode {
return new HeadingNode(tag);
}
to
function $createHeadingNode(tag): HeadingNode {
const NodeConstructor = $getNodeFromRegistry(HeadingNode);
return new NodeConstructor(tag);
}
...
// and smth like:
function $getNodeFromRegistry<T>(nodeKlass: T): T {
return $getActiveEditor()._registeredNodes.get(nodeKlass.getType());
}
This will allow passing AnchorHeadingNode into LexicalComposer, and since it has the same type ('heading'), it'll be used whenever $createHeadingNode is used, so all callsites that create heading will use our extended heading node.
Problem
Plugins and nodes are tightly coupled and it makes it harder to extend default nodes and maintaining existing plugins functionality.
Example
Let's take markdown plugin (copy/paste logic is also a good example), it exports bunch of node creation helpers (
$createHeadingNode,$createCodeBlockNode,$createListNode, etc).Then if I want to extend HeadingNode behaviour, for example to add
idattribute to each heading so it can be used as an anchor (smth like<h1 id="getting-started-with-react">Getting started with React</h1>, see our README doing it). To do so I'd extend default node and add new behaviour on top:But now the problem is that all plugins that might insert HeadingNode (markdown, toolbar, copy/paste), they all keep using default HeadingNode, because they all use
$createHeadingNodeimported from default heading node file.Potential solution:
Replace
$createHeadingNode()implementation fromto
This will allow passing
AnchorHeadingNodeinto LexicalComposer, and since it has the same type ('heading'), it'll be used whenever $createHeadingNode is used, so all callsites that create heading will use our extended heading node.