rittenhop-dev/versions/5.94.2/node_modules/@tryghost/kg-default-nodes/lib/nodes/ExtendedTextNode.js

121 lines
4.0 KiB
JavaScript
Raw Normal View History

2024-09-23 19:40:12 -04:00
/* eslint-disable ghost/filenames/match-exported-class */
import {$isTextNode, TextNode} from 'lexical';
// Since the TextNode is foundational to all Lexical packages, including the
// plain text use case. Handling any rich text logic is undesirable. This creates
// the need to override the TextNode to handle serialization and deserialization
// of HTML/CSS styling properties to achieve full fidelity between JSON <-> HTML.
//
// https://lexical.dev/docs/concepts/serialization#handling-extended-html-styling
export const extendedTextNodeReplacement = {replace: TextNode, with: node => new ExtendedTextNode(node.__text)};
export class ExtendedTextNode extends TextNode {
constructor(text, key) {
super(text, key);
}
static getType() {
return 'extended-text';
}
static clone(node) {
return new ExtendedTextNode(node.__text, node.__key);
}
static importDOM() {
const importers = TextNode.importDOM();
return {
...importers,
span: () => ({
conversion: patchConversion(importers?.span, convertSpanElement),
priority: 1
})
};
}
static importJSON(serializedNode) {
return TextNode.importJSON(serializedNode);
}
exportJSON() {
const json = super.exportJSON();
json.type = 'extended-text';
return json;
}
isSimpleText() {
return (
(this.__type === 'text' || this.__type === 'extended-text') &&
this.__mode === 0
);
}
isInline() {
return true;
}
}
function patchConversion(originalDOMConverter, convertFn) {
return (node) => {
const original = originalDOMConverter?.(node);
if (!original) {
return null;
}
const originalOutput = original.conversion(node);
if (!originalOutput) {
return originalOutput;
}
return {
...originalOutput,
forChild: (lexicalNode, parent) => {
const originalForChild = originalOutput?.forChild ?? (x => x);
const result = originalForChild(lexicalNode, parent);
if ($isTextNode(result)) {
return convertFn(result, node);
}
return result;
}
};
};
}
function convertSpanElement(lexicalNode, domNode) {
const span = domNode;
// Word uses span tags + font-weight for bold text
const hasBoldFontWeight = span.style.fontWeight === 'bold' || span.parentElement?.style.fontWeight === 'bold';
// Word uses span tags + font-style for italic text
const hasItalicFontStyle = span.style.fontStyle === 'italic' || span.parentElement?.style.fontStyle === 'italic';
// Word uses span tags + text-decoration for underline text
const hasUnderlineTextDecoration = span.style.textDecoration === 'underline' || span.parentElement?.style.textDecoration === 'underline';
// Word uses span tags + "Strikethrough" class for strikethrough text
const hasStrikethroughClass = span.classList.contains('Strikethrough') || span.parentElement?.classList.contains('Strikethrough');
// Word uses span tags + "Highlight" class for highlighted text
const hasHighlightClass = span.classList.contains('Highlight') || span.parentElement?.classList.contains('Highlight');
if (hasBoldFontWeight && !lexicalNode.hasFormat('bold')) {
lexicalNode = lexicalNode.toggleFormat('bold');
}
if (hasItalicFontStyle && !lexicalNode.hasFormat('italic')) {
lexicalNode = lexicalNode.toggleFormat('italic');
}
if (hasUnderlineTextDecoration && !lexicalNode.hasFormat('underline')) {
lexicalNode = lexicalNode.toggleFormat('underline');
}
if (hasStrikethroughClass && !lexicalNode.hasFormat('strikethrough')) {
lexicalNode = lexicalNode.toggleFormat('strikethrough');
}
if (hasHighlightClass && !lexicalNode.hasFormat('highlight')) {
lexicalNode = lexicalNode.toggleFormat('highlight');
}
return lexicalNode;
}