Lexical: Added table cell node import logic

This commit is contained in:
Dan Brown 2024-08-06 09:36:37 +01:00
parent b3d3b14f79
commit fcc1c2968d
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
2 changed files with 139 additions and 6 deletions

View File

@ -1,7 +1,24 @@
import {EditorConfig} from "lexical/LexicalEditor"; import {
import {DOMExportOutput, LexicalEditor, LexicalNode, Spread} from "lexical"; $createParagraphNode,
$isElementNode,
$isLineBreakNode,
$isTextNode,
DOMConversionMap,
DOMConversionOutput,
DOMExportOutput,
EditorConfig,
LexicalEditor,
LexicalNode,
Spread
} from "lexical";
import {SerializedTableCellNode, TableCellHeaderStates, TableCellNode} from "@lexical/table"; import {
$createTableCellNode,
$isTableCellNode,
SerializedTableCellNode,
TableCellHeaderStates,
TableCellNode
} from "@lexical/table";
import {TableCellHeaderState} from "@lexical/table/LexicalTableCellNode"; import {TableCellHeaderState} from "@lexical/table/LexicalTableCellNode";
export type SerializedCustomTableCellNode = Spread<{ export type SerializedCustomTableCellNode = Spread<{
@ -54,13 +71,24 @@ export class CustomTableCellNode extends TableCellNode {
return element; return element;
} }
// TODO - Import DOM
updateDOM(prevNode: CustomTableCellNode): boolean { updateDOM(prevNode: CustomTableCellNode): boolean {
return super.updateDOM(prevNode) return super.updateDOM(prevNode)
|| this.__styles !== prevNode.__styles; || this.__styles !== prevNode.__styles;
} }
static importDOM(): DOMConversionMap | null {
return {
td: (node: Node) => ({
conversion: $convertCustomTableCellNodeElement,
priority: 0,
}),
th: (node: Node) => ({
conversion: $convertCustomTableCellNodeElement,
priority: 0,
}),
};
}
exportDOM(editor: LexicalEditor): DOMExportOutput { exportDOM(editor: LexicalEditor): DOMExportOutput {
const element = this.createDOM(editor._config); const element = this.createDOM(editor._config);
return { return {
@ -68,6 +96,18 @@ export class CustomTableCellNode extends TableCellNode {
}; };
} }
static importJSON(serializedNode: SerializedCustomTableCellNode): CustomTableCellNode {
const node = $createCustomTableCellNode(
serializedNode.headerState,
serializedNode.colSpan,
serializedNode.width,
);
node.setStyles(new Map<string, string>(Object.entries(serializedNode.styles)));
return node;
}
exportJSON(): SerializedCustomTableCellNode { exportJSON(): SerializedCustomTableCellNode {
return { return {
...super.exportJSON(), ...super.exportJSON(),
@ -77,6 +117,100 @@ export class CustomTableCellNode extends TableCellNode {
} }
} }
function $convertCustomTableCellNodeElement(domNode: Node): DOMConversionOutput {
const output = $convertTableCellNodeElement(domNode);
if (domNode instanceof HTMLElement && output.node instanceof CustomTableCellNode) {
const styleMap = new Map<string, string>();
const styleNames = Array.from(domNode.style);
for (const style of styleNames) {
styleMap.set(style, domNode.style.getPropertyValue(style));
}
output.node.setStyles(styleMap);
}
return output;
}
/**
* Function taken from:
* https://github.com/facebook/lexical/blob/e1881a6e409e1541c10dd0b5378f3a38c9dc8c9e/packages/lexical-table/src/LexicalTableCellNode.ts#L289
* Copyright (c) Meta Platforms, Inc. and affiliates.
* MIT LICENSE
* Modified since copy.
*/
export function $convertTableCellNodeElement(
domNode: Node,
): DOMConversionOutput {
const domNode_ = domNode as HTMLTableCellElement;
const nodeName = domNode.nodeName.toLowerCase();
let width: number | undefined = undefined;
const PIXEL_VALUE_REG_EXP = /^(\d+(?:\.\d+)?)px$/;
if (PIXEL_VALUE_REG_EXP.test(domNode_.style.width)) {
width = parseFloat(domNode_.style.width);
}
const tableCellNode = $createTableCellNode(
nodeName === 'th'
? TableCellHeaderStates.ROW
: TableCellHeaderStates.NO_STATUS,
domNode_.colSpan,
width,
);
tableCellNode.__rowSpan = domNode_.rowSpan;
const style = domNode_.style;
const textDecoration = style.textDecoration.split(' ');
const hasBoldFontWeight =
style.fontWeight === '700' || style.fontWeight === 'bold';
const hasLinethroughTextDecoration = textDecoration.includes('line-through');
const hasItalicFontStyle = style.fontStyle === 'italic';
const hasUnderlineTextDecoration = textDecoration.includes('underline');
return {
after: (childLexicalNodes) => {
if (childLexicalNodes.length === 0) {
childLexicalNodes.push($createParagraphNode());
}
return childLexicalNodes;
},
forChild: (lexicalNode, parentLexicalNode) => {
if ($isTableCellNode(parentLexicalNode) && !$isElementNode(lexicalNode)) {
const paragraphNode = $createParagraphNode();
if (
$isLineBreakNode(lexicalNode) &&
lexicalNode.getTextContent() === '\n'
) {
return null;
}
if ($isTextNode(lexicalNode)) {
if (hasBoldFontWeight) {
lexicalNode.toggleFormat('bold');
}
if (hasLinethroughTextDecoration) {
lexicalNode.toggleFormat('strikethrough');
}
if (hasItalicFontStyle) {
lexicalNode.toggleFormat('italic');
}
if (hasUnderlineTextDecoration) {
lexicalNode.toggleFormat('underline');
}
}
paragraphNode.append(lexicalNode);
return paragraphNode;
}
return lexicalNode;
},
node: tableCellNode,
};
}
export function $createCustomTableCellNode( export function $createCustomTableCellNode(
headerState: TableCellHeaderState, headerState: TableCellHeaderState,
colSpan = 1, colSpan = 1,

View File

@ -3,7 +3,6 @@
## In progress ## In progress
- Table features - Table features
- CustomTableCellNode importDOM logic
- Merge cell action - Merge cell action
- Row properties form logic - Row properties form logic
- Table properties form logic - Table properties form logic