Skip to content

Instantly share code, notes, and snippets.

@davidnus
Created March 25, 2025 12:08
Show Gist options
  • Select an option

  • Save davidnus/04da9d3dc693fd64c8b62944ef1b3d1f to your computer and use it in GitHub Desktop.

Select an option

Save davidnus/04da9d3dc693fd64c8b62944ef1b3d1f to your computer and use it in GitHub Desktop.
import { TextNode } from 'lexical';
/***
* Example of custom warning node.
* This node adds the additional properties for rendering on top of
* a basic Lexical TextNode.
*/
export class WarningNode extends TextNode {
/**
* Assume you want to add the following attributes to an extended node
*/
__id; //some custom id that's immutable
__color; //color attribute that holds a string, and is mutable
__backgroundColor; //background color attribute that holds a string, and is mutable
/*
* Override TextNode getType
*/
static getType() {
return 'warning'; //give your node a custom type
}
/*
* Override TextNode clone
*/
static clone(node) {
return new WarningNode(node.__id, node.__color, node.__backgroundColor, node.__text, node.__key);
}
/*
* Override TextNode importJSON
*/
static importJSON(serializedNode) {
/**
* this is a common recipe to import a custom json text-like node in lexical:
*/
const node = $createWarningNode(serializedNode.id, serializedNode.color, serializedNode.backgroundColor);
node.setTextContent(serializedNode.text);
node.setFormat(serializedNode.format);
node.setDetail(serializedNode.detail);
node.setMode(serializedNode.mode);
node.setStyle(serializedNode.style);
return node;
}
/*
* Override TextNode constructor
*/
constructor(id, color, backgroundColor, text, key) {
// invoke parent constructor
super(text, key);
// extension: set custom attributes, with default values
this.__id = id || 'mock_id';
this.__color = color || '#ffffff';
this.__backgroundColor = backgroundColor || '#ff4902';
}
/*
* Override TextNode exportJSON
*/
exportJSON() {
return {
// invoke parent text node export
...super.exportJSON(),
version: 1,
/**
* extension: attach custom properties to the exported node
*/
id: this.__id,
color: this.__color,
backgroundColor: this.__backgroundColor,
type: 'warning',
};
}
/*
* Override TextNode createDOM
*/
createDOM(config) {
//Invoke parent createDOM
const dom = super.createDOM(config);
/**
* extension: Compose custom css styles string from custom attributes
*/
dom.style.cssText = `color:${this.__color};background-color:${this.__backgroundColor};`;
dom.className = 'warning-text-frame';
return dom;
}
updateDOM() {
return true;
}
//optional: getters and setters
getColor() {
return this.__color;
}
setColor(color) {
const writable = this.getWritable();
writable.__color = color;
}
getBackgroundColor() {
return this.__backgroundColor;
}
setBackgroundColor(backgroundColor) {
const writable = this.getWritable();
writable.__backgroundColor = backgroundColor;
}
/**
* Implement the following API methods to interoperate with Codox
* Lexical -> Codox Node
* Converts lexical node to json node, acceptable by Codox.
*/
toCodoxNode() {
/**
* Step 1: Get original text node json. You should/must use .exportJSON()
*/
const originalJsonNode = this.exportJSON();
/**
* Step 2: Extend with additional properties.
*/
const convertedNode = {
type: 'text', // type property must be one of core or playground node types
// preserve default text node attributes
text: originalJsonNode.text,
format: originalJsonNode.format,
mode: originalJsonNode.mode,
detail: originalJsonNode.detail,
version: originalJsonNode.version,
// extension: all mutable style properties should be serialized as a style string, as per TextNode definition
// assumption is the lexical node style is empty, if not, concat the styles.
style: `color:${originalJsonNode.color};background-color:${originalJsonNode.backgroundColor};`,
/**
* extension: codox_metadata should hold immutables:
* - required: your extended node type, e.g. 'warning'
* - optional: any immutable properties of an original node - these fields are not merged and synchronized.
*/
codox_metadata: {
type: originalJsonNode.type, //type == 'warning'
id: originalJsonNode.id, //some custom id an immutable property
},
};
console.log('[DEMO DEBUG][WarningNode][toCodoxNode]: ', { convertedNode, originalJsonNode });
return convertedNode;
}
/**
* Implement the following API methods to interoperate with Codox
* Codox -> Lexical Node
* Converts synchronized codox json node to lexical json node
* must be class 'static' method
*/
static fromCodoxNode(codoxNode) {
/**
* extension: all mutable properties should be deserialized from the style string,
*/
let color = '';
let backgroundColor = '';
codoxNode.style.split(';').forEach((css) => {
if (css.startsWith('color:')) {
color = css.split(':')[1];
}
if (css.startsWith('background-color:')) {
backgroundColor = css.split(':')[1];
}
});
// compose original json node from codox node
const originalJsonNode = {
// set original type - in this example it will be "warning"
type: codoxNode.codox_metadata.type,
// set base attributes
text: codoxNode.text,
format: codoxNode.format,
mode: codoxNode.mode,
detail: codoxNode.detail,
version: codoxNode.version,
style: '', // assumption here is the lexical node style is empty, if not, splice out the extended mutable properties
/**
* set custom attributes
*/
id: codoxNode.codox_metadata.id,
color: color,
backgroundColor: backgroundColor,
};
console.log('[DEMO DEBUG][WarningNode][fromCodoxNode]: ', { codoxNode, originalJsonNode });
return originalJsonNode;
}
}
// optional: best practice
export function $createWarningNode(id, color, backgroundColor, text, key) {
const warntextNode = new WarningNode(id, color, backgroundColor, text, key);
return warntextNode;
}
export function $isWarningNode(node) {
return node instanceof WarningNode;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment