Skip to content

Decoupling plugin and nodes #1262

@fantactuka

Description

@fantactuka

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.

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions