@lexical/table
Version:
This package provides the Table feature for Lexical.
158 lines (129 loc) • 3.56 kB
text/typescript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import {$descendantsMatching} from '@lexical/utils';
import {
$applyNodeReplacement,
$getDocument,
addClassNamesToElement,
type BaseSelection,
type DOMConversionMap,
type DOMConversionOutput,
type EditorConfig,
ElementNode,
type LexicalNode,
type LexicalUpdateJSON,
type NodeKey,
type SerializedElementNode,
type Spread,
} from 'lexical';
import {PIXEL_VALUE_REG_EXP} from './constants';
import {$isTableCellNode} from './LexicalTableCellNode';
export type SerializedTableRowNode = Spread<
{
height?: number;
},
SerializedElementNode
>;
/** @noInheritDoc */
export class TableRowNode extends ElementNode {
/** @internal */
__height?: number;
static getType(): string {
return 'tablerow';
}
static clone(node: TableRowNode): TableRowNode {
return new TableRowNode(node.__height, node.__key);
}
afterCloneFrom(prevNode: this): void {
super.afterCloneFrom(prevNode);
this.__height = prevNode.__height;
}
static importDOM(): DOMConversionMap | null {
return {
tr: (node: Node) => ({
conversion: $convertTableRowElement,
priority: 0,
}),
};
}
static importJSON(serializedNode: SerializedTableRowNode): TableRowNode {
return $createTableRowNode().updateFromJSON(serializedNode);
}
updateFromJSON(
serializedNode: LexicalUpdateJSON<SerializedTableRowNode>,
): this {
return super
.updateFromJSON(serializedNode)
.setHeight(serializedNode.height);
}
constructor(height?: number, key?: NodeKey) {
super(key);
this.__height = height;
}
exportJSON(): SerializedTableRowNode {
const height = this.getHeight();
return {
...super.exportJSON(),
...(height === undefined ? undefined : {height}),
};
}
createDOM(config: EditorConfig): HTMLElement {
const element = $getDocument().createElement('tr');
if (this.__height) {
element.style.height = `${this.__height}px`;
}
addClassNamesToElement(element, config.theme.tableRow);
return element;
}
extractWithChild(
child: LexicalNode,
selection: BaseSelection | null,
destination: 'clone' | 'html',
): boolean {
return destination === 'html';
}
isShadowRoot(): boolean {
return true;
}
setHeight(height?: number | undefined): this {
const self = this.getWritable();
self.__height = height;
return self;
}
getHeight(): number | undefined {
return this.getLatest().__height;
}
updateDOM(prevNode: this): boolean {
return prevNode.__height !== this.__height;
}
canBeEmpty(): false {
return false;
}
canIndent(): false {
return false;
}
}
export function $convertTableRowElement(domNode: Node): DOMConversionOutput {
const domNode_ = domNode as HTMLTableCellElement;
let height: number | undefined = undefined;
if (PIXEL_VALUE_REG_EXP.test(domNode_.style.height)) {
height = parseFloat(domNode_.style.height);
}
return {
after: children => $descendantsMatching(children, $isTableCellNode),
node: $createTableRowNode(height),
};
}
export function $createTableRowNode(height?: number): TableRowNode {
return $applyNodeReplacement(new TableRowNode(height));
}
export function $isTableRowNode(
node: LexicalNode | null | undefined,
): node is TableRowNode {
return node instanceof TableRowNode;
}