2 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
14 } from '../LexicalEditor';
21 } from '../LexicalNode';
22 import {RangeSelection, TEXT_TYPE_TO_FORMAT, TextFormatType} from 'lexical';
25 $applyNodeReplacement,
26 getCachedClassNameArray,
28 } from '../LexicalUtils';
29 import {$isTextNode} from './LexicalTextNode';
31 commonPropertiesDifferent, deserializeCommonBlockNode,
32 setCommonBlockPropsFromElement,
33 updateElementWithCommonBlockProps
35 import {CommonBlockNode, copyCommonBlockProperties, SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
37 export type SerializedParagraphNode = Spread<
42 SerializedCommonBlockNode
46 export class ParagraphNode extends CommonBlockNode {
47 ['constructor']!: KlassConstructor<typeof ParagraphNode>;
52 constructor(key?: NodeKey) {
54 this.__textFormat = 0;
55 this.__textStyle = '';
58 static getType(): string {
62 getTextFormat(): number {
63 const self = this.getLatest();
64 return self.__textFormat;
67 setTextFormat(type: number): this {
68 const self = this.getWritable();
69 self.__textFormat = type;
73 hasTextFormat(type: TextFormatType): boolean {
74 const formatFlag = TEXT_TYPE_TO_FORMAT[type];
75 return (this.getTextFormat() & formatFlag) !== 0;
78 getTextStyle(): string {
79 const self = this.getLatest();
80 return self.__textStyle;
83 setTextStyle(style: string): this {
84 const self = this.getWritable();
85 self.__textStyle = style;
89 static clone(node: ParagraphNode): ParagraphNode {
90 return new ParagraphNode(node.__key);
93 afterCloneFrom(prevNode: this) {
94 super.afterCloneFrom(prevNode);
95 this.__textFormat = prevNode.__textFormat;
96 this.__textStyle = prevNode.__textStyle;
97 copyCommonBlockProperties(prevNode, this);
102 createDOM(config: EditorConfig): HTMLElement {
103 const dom = document.createElement('p');
104 const classNames = getCachedClassNameArray(config.theme, 'paragraph');
105 if (classNames !== undefined) {
106 const domClassList = dom.classList;
107 domClassList.add(...classNames);
110 updateElementWithCommonBlockProps(dom, this);
115 prevNode: ParagraphNode,
117 config: EditorConfig,
119 return commonPropertiesDifferent(prevNode, this);
122 static importDOM(): DOMConversionMap | null {
124 p: (node: Node) => ({
125 conversion: $convertParagraphElement,
131 exportDOM(editor: LexicalEditor): DOMExportOutput {
132 const {element} = super.exportDOM(editor);
134 if (element && isHTMLElement(element)) {
135 if (this.isEmpty()) {
136 element.append(document.createElement('br'));
145 static importJSON(serializedNode: SerializedParagraphNode): ParagraphNode {
146 const node = $createParagraphNode();
147 deserializeCommonBlockNode(serializedNode, node);
148 node.setTextFormat(serializedNode.textFormat);
152 exportJSON(): SerializedParagraphNode {
154 ...super.exportJSON(),
155 textFormat: this.getTextFormat(),
156 textStyle: this.getTextStyle(),
165 rangeSelection: RangeSelection,
166 restoreSelection: boolean,
168 const newElement = $createParagraphNode();
169 newElement.setTextFormat(rangeSelection.format);
170 newElement.setTextStyle(rangeSelection.style);
171 const direction = this.getDirection();
172 newElement.setDirection(direction);
173 newElement.setStyle(this.getTextStyle());
174 this.insertAfter(newElement, restoreSelection);
178 collapseAtStart(): boolean {
179 const children = this.getChildren();
180 // If we have an empty (trimmed) first paragraph and try and remove it,
181 // delete the paragraph as long as we have another sibling to go to
183 children.length === 0 ||
184 ($isTextNode(children[0]) && children[0].getTextContent().trim() === '')
186 const nextSibling = this.getNextSibling();
187 if (nextSibling !== null) {
192 const prevSibling = this.getPreviousSibling();
193 if (prevSibling !== null) {
194 this.selectPrevious();
203 function $convertParagraphElement(element: HTMLElement): DOMConversionOutput {
204 const node = $createParagraphNode();
205 setCommonBlockPropsFromElement(element, node);
209 export function $createParagraphNode(): ParagraphNode {
210 return $applyNodeReplacement(new ParagraphNode());
213 export function $isParagraphNode(
214 node: LexicalNode | null | undefined,
215 ): node is ParagraphNode {
216 return node instanceof ParagraphNode;