From f025eb639f553106f737479017a1fae4468700c6 Mon Sep 17 00:00:00 2001 From: "C." Date: Mon, 2 Dec 2024 18:22:09 -0600 Subject: [PATCH 01/23] Add text format to listitemnode --- .../lexical-list/src/LexicalListItemNode.ts | 43 ++++++++++++++++++- packages/lexical-list/src/utils.ts | 24 ++++++++++- packages/lexical/src/LexicalSelection.ts | 12 ++++++ 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index f4fafcba71a..27c92cc0b88 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -14,12 +14,14 @@ import type { DOMExportOutput, EditorConfig, EditorThemeClasses, + KlassConstructor, LexicalNode, NodeKey, ParagraphNode, RangeSelection, SerializedElementNode, Spread, + TextFormatType, } from 'lexical'; import { @@ -34,28 +36,32 @@ import { $isRangeSelection, ElementNode, LexicalEditor, + TEXT_TYPE_TO_FORMAT, } from 'lexical'; import invariant from 'shared/invariant'; import normalizeClassNames from 'shared/normalizeClassNames'; import {$createListNode, $isListNode} from './'; import {$handleIndent, $handleOutdent, mergeLists} from './formatList'; -import {isNestedListNode} from './utils'; +import {isNestedListNode, toggleTextFormatType} from './utils'; export type SerializedListItemNode = Spread< { checked: boolean | undefined; value: number; + textFormat: number; }, SerializedElementNode >; /** @noInheritDoc */ export class ListItemNode extends ElementNode { + ['constructor']!: KlassConstructor; /** @internal */ __value: number; /** @internal */ __checked?: boolean; + __textFormat: number; static getType(): string { return 'listitem'; @@ -69,6 +75,32 @@ export class ListItemNode extends ElementNode { super(key); this.__value = value === undefined ? 1 : value; this.__checked = checked; + this.__textFormat = 0; + } + getTextFormat(): number { + const self = this.getLatest(); + return self.__textFormat; + } + + setTextFormat(type: number): this { + const self = this.getWritable(); + self.__textFormat = type; + return self; + } + + hasTextFormat(type: TextFormatType): boolean { + const formatFlag = TEXT_TYPE_TO_FORMAT[type]; + return (this.getTextFormat() & formatFlag) !== 0; + } + + getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number { + const self = this.getLatest(); + const format = self.__textFormat; + return toggleTextFormatType(format, type, alignWithFormat); + } + afterCloneFrom(prevNode: this) { + super.afterCloneFrom(prevNode); + this.__textFormat = prevNode.__textFormat; } createDOM(config: EditorConfig): HTMLElement { @@ -128,6 +160,7 @@ export class ListItemNode extends ElementNode { node.setValue(serializedNode.value); node.setFormat(serializedNode.format); node.setDirection(serializedNode.direction); + node.setTextFormat(serializedNode.textFormat); return node; } @@ -143,6 +176,7 @@ export class ListItemNode extends ElementNode { return { ...super.exportJSON(), checked: this.getChecked(), + textFormat: this.getTextFormat(), type: 'listitem', value: this.getValue(), version: 1, @@ -256,12 +290,17 @@ export class ListItemNode extends ElementNode { } insertNewAfter( - _: RangeSelection, + selection: RangeSelection, restoreSelection = true, ): ListItemNode | ParagraphNode { const newElement = $createListItemNode( this.__checked == null ? undefined : false, ); + + const format = selection.format; + newElement.setTextFormat(format); + + newElement.setFormat(this.getFormatType()); this.insertAfter(newElement, restoreSelection); return newElement; diff --git a/packages/lexical-list/src/utils.ts b/packages/lexical-list/src/utils.ts index 9c9b1bf9af1..e0d6e149cb7 100644 --- a/packages/lexical-list/src/utils.ts +++ b/packages/lexical-list/src/utils.ts @@ -6,9 +6,8 @@ * */ -import type {LexicalNode, Spread} from 'lexical'; - import {$findMatchingParent} from '@lexical/utils'; +import {type LexicalNode, type Spread, TEXT_TYPE_TO_FORMAT} from 'lexical'; import invariant from 'shared/invariant'; import { @@ -203,3 +202,24 @@ export function $wrapInListItem(node: LexicalNode): ListItemNode { const listItemWrapper = $createListItemNode(); return listItemWrapper.append(node); } + +export function toggleTextFormatType( + format: number, + type: TextFormatType, + alignWithFormat: null | number, +): number { + const activeFormat = TEXT_TYPE_TO_FORMAT[type]; + if ( + alignWithFormat !== null && + (format & activeFormat) === (alignWithFormat & activeFormat) + ) { + return format; + } + let newFormat = format ^ activeFormat; + if (type === 'subscript') { + newFormat &= ~TEXT_TYPE_TO_FORMAT.superscript; + } else if (type === 'superscript') { + newFormat &= ~TEXT_TYPE_TO_FORMAT.subscript; + } + return newFormat; +} diff --git a/packages/lexical/src/LexicalSelection.ts b/packages/lexical/src/LexicalSelection.ts index fb7a62dbea0..34ad7610199 100644 --- a/packages/lexical/src/LexicalSelection.ts +++ b/packages/lexical/src/LexicalSelection.ts @@ -12,6 +12,7 @@ import type {NodeKey} from './LexicalNode'; import type {ElementNode} from './nodes/LexicalElementNode'; import type {TextFormatType} from './nodes/LexicalTextNode'; +import {$isListItemNode} from '@lexical/list'; import invariant from 'shared/invariant'; import { @@ -1214,12 +1215,22 @@ export class RangeSelection implements BaseSelection { }); }; + const applyFormatToListItems = (alignWith: number | null) => { + selectedNodes.forEach((node) => { + if ($isListItemNode(node)) { + const newFormat = node.getFormatFlags(formatType, alignWith); + node.setTextFormat(newFormat); + } + }); + }; + const selectedTextNodesLength = selectedTextNodes.length; if (selectedTextNodesLength === 0) { this.toggleFormat(formatType); // When changing format, we should stop composition $setCompositionKey(null); applyFormatToParagraphs(alignWithFormat); + applyFormatToListItems(alignWithFormat); return; } @@ -1252,6 +1263,7 @@ export class RangeSelection implements BaseSelection { alignWithFormat, ); applyFormatToParagraphs(firstNextFormat); + applyFormatToListItems(firstNextFormat); const lastIndex = selectedTextNodesLength - 1; let lastNode = selectedTextNodes[lastIndex]; From 8540c747731ed3b64aed4cde1d00ba4d90249020 Mon Sep 17 00:00:00 2001 From: "C." Date: Tue, 3 Dec 2024 14:38:17 -0600 Subject: [PATCH 02/23] Revert changes in LexicalSelection file --- packages/lexical/src/LexicalSelection.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/lexical/src/LexicalSelection.ts b/packages/lexical/src/LexicalSelection.ts index 34ad7610199..fb7a62dbea0 100644 --- a/packages/lexical/src/LexicalSelection.ts +++ b/packages/lexical/src/LexicalSelection.ts @@ -12,7 +12,6 @@ import type {NodeKey} from './LexicalNode'; import type {ElementNode} from './nodes/LexicalElementNode'; import type {TextFormatType} from './nodes/LexicalTextNode'; -import {$isListItemNode} from '@lexical/list'; import invariant from 'shared/invariant'; import { @@ -1215,22 +1214,12 @@ export class RangeSelection implements BaseSelection { }); }; - const applyFormatToListItems = (alignWith: number | null) => { - selectedNodes.forEach((node) => { - if ($isListItemNode(node)) { - const newFormat = node.getFormatFlags(formatType, alignWith); - node.setTextFormat(newFormat); - } - }); - }; - const selectedTextNodesLength = selectedTextNodes.length; if (selectedTextNodesLength === 0) { this.toggleFormat(formatType); // When changing format, we should stop composition $setCompositionKey(null); applyFormatToParagraphs(alignWithFormat); - applyFormatToListItems(alignWithFormat); return; } @@ -1263,7 +1252,6 @@ export class RangeSelection implements BaseSelection { alignWithFormat, ); applyFormatToParagraphs(firstNextFormat); - applyFormatToListItems(firstNextFormat); const lastIndex = selectedTextNodesLength - 1; let lastNode = selectedTextNodes[lastIndex]; From 539c8bb334b666488383b39a1f803a624d0bdcd6 Mon Sep 17 00:00:00 2001 From: "C." Date: Tue, 3 Dec 2024 15:34:18 -0600 Subject: [PATCH 03/23] Change ListItemNode to inherit from ParagraphNode --- .../lexical-list/src/LexicalListItemNode.ts | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 27c92cc0b88..0586eeb69e5 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -7,22 +7,6 @@ */ import type {ListNode, ListType} from './'; -import type { - BaseSelection, - DOMConversionMap, - DOMConversionOutput, - DOMExportOutput, - EditorConfig, - EditorThemeClasses, - KlassConstructor, - LexicalNode, - NodeKey, - ParagraphNode, - RangeSelection, - SerializedElementNode, - Spread, - TextFormatType, -} from 'lexical'; import { addClassNamesToElement, @@ -34,9 +18,23 @@ import { $isElementNode, $isParagraphNode, $isRangeSelection, + BaseSelection, + DOMConversionMap, + DOMConversionOutput, + DOMExportOutput, + EditorConfig, + EditorThemeClasses, ElementNode, + KlassConstructor, LexicalEditor, + LexicalNode, + NodeKey, + ParagraphNode, + RangeSelection, + SerializedElementNode, + Spread, TEXT_TYPE_TO_FORMAT, + TextFormatType, } from 'lexical'; import invariant from 'shared/invariant'; import normalizeClassNames from 'shared/normalizeClassNames'; @@ -55,7 +53,7 @@ export type SerializedListItemNode = Spread< >; /** @noInheritDoc */ -export class ListItemNode extends ElementNode { +export class ListItemNode extends ParagraphNode { ['constructor']!: KlassConstructor; /** @internal */ __value: number; From 9f36497e612b62efb71f154efd95699ae18de72c Mon Sep 17 00:00:00 2001 From: "C." Date: Tue, 3 Dec 2024 17:23:06 -0600 Subject: [PATCH 04/23] Add e2e test to fix list indent format bug fix --- .../__tests__/e2e/List.spec.mjs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/packages/lexical-playground/__tests__/e2e/List.spec.mjs b/packages/lexical-playground/__tests__/e2e/List.spec.mjs index 8c1703c15c9..2020899a34b 100644 --- a/packages/lexical-playground/__tests__/e2e/List.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/List.spec.mjs @@ -17,6 +17,7 @@ import { redo, selectAll, selectCharacters, + toggleBold, undo, } from '../keyboardShortcuts/index.mjs'; import { @@ -1880,4 +1881,48 @@ test.describe.parallel('Nested List', () => { }); }, ); + test('new list item should preserve format from previous list item even after new list item is indented', async ({ + page, + }) => { + await focusEditor(page); + await toggleBulletList(page); + await toggleBold(page); + await page.keyboard.type('MLH Fellowship'); + await page.keyboard.press('Enter'); + await clickIndentButton(page); + await page.keyboard.type('Fall 2024'); + await assertHTML( + page, + html` +
    +
  • + + MLH Fellowship + +
  • +
  • +
      +
    • + + Fall 2024 + +
    • +
    +
  • +
+ `, + ); + }); }); From 4d745ab13222cccd0b69f314d9ee9c76df8d6ea6 Mon Sep 17 00:00:00 2001 From: "C." Date: Wed, 4 Dec 2024 18:52:07 -0600 Subject: [PATCH 05/23] remove klass constructor line from lexicallistitemnode --- packages/lexical-list/src/LexicalListItemNode.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 0586eeb69e5..a7cbc8e90b1 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -25,7 +25,6 @@ import { EditorConfig, EditorThemeClasses, ElementNode, - KlassConstructor, LexicalEditor, LexicalNode, NodeKey, @@ -54,7 +53,6 @@ export type SerializedListItemNode = Spread< /** @noInheritDoc */ export class ListItemNode extends ParagraphNode { - ['constructor']!: KlassConstructor; /** @internal */ __value: number; /** @internal */ From 43b95a9dd726433e912ef3f7615d6ae8b7a684e5 Mon Sep 17 00:00:00 2001 From: "C." Date: Thu, 5 Dec 2024 12:31:31 -0600 Subject: [PATCH 06/23] Adjust LexicalListItemNode class and use of it to the change of inheriting from paragraphnode --- .../lexical-list/src/LexicalListItemNode.ts | 58 +++++++------------ packages/lexical-list/src/formatList.ts | 22 +++---- 2 files changed, 32 insertions(+), 48 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index a7cbc8e90b1..c4f176de743 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -30,7 +30,7 @@ import { NodeKey, ParagraphNode, RangeSelection, - SerializedElementNode, + SerializedParagraphNode, Spread, TEXT_TYPE_TO_FORMAT, TextFormatType, @@ -48,7 +48,7 @@ export type SerializedListItemNode = Spread< value: number; textFormat: number; }, - SerializedElementNode + SerializedParagraphNode >; /** @noInheritDoc */ @@ -94,10 +94,6 @@ export class ListItemNode extends ParagraphNode { const format = self.__textFormat; return toggleTextFormatType(format, type, alignWithFormat); } - afterCloneFrom(prevNode: this) { - super.afterCloneFrom(prevNode); - this.__textFormat = prevNode.__textFormat; - } createDOM(config: EditorConfig): HTMLElement { const element = document.createElement('li'); @@ -109,21 +105,20 @@ export class ListItemNode extends ParagraphNode { $setListItemThemeClassNames(element, config.theme, this); return element; } - updateDOM( - prevNode: ListItemNode, + prevNode: ParagraphNode, dom: HTMLElement, config: EditorConfig, ): boolean { const parent = this.getParent(); if ($isListNode(parent) && parent.getListType() === 'check') { - updateListItemChecked(dom, this, prevNode, parent); + const listItemNode = $isListItemNode(prevNode) ? prevNode : null; + updateListItemChecked(dom, this, listItemNode, parent); } // @ts-expect-error - this is always HTMLListItemElement dom.value = this.__value; $setListItemThemeClassNames(dom, config.theme, this); - - return false; + return super.updateDOM(prevNode, dom, config); } static transform(): (node: LexicalNode) => void { @@ -254,15 +249,11 @@ export class ListItemNode extends ParagraphNode { } const siblings = this.getNextSiblings(); - - // Split the lists and insert the node in between them listNode.insertAfter(node, restoreSelection); if (siblings.length !== 0) { const newListNode = $createListNode(listNode.getListType()); - siblings.forEach((sibling) => newListNode.append(sibling)); - node.insertAfter(newListNode, restoreSelection); } @@ -302,40 +293,33 @@ export class ListItemNode extends ParagraphNode { return newElement; } - collapseAtStart(selection: RangeSelection): true { + collapseAtStart(): boolean { + const selection = $getSelection(); + + if (!$isRangeSelection(selection)) { + return false; + } + const paragraph = $createParagraphNode(); const children = this.getChildren(); children.forEach((child) => paragraph.append(child)); + const listNode = this.getParentOrThrow(); - const listNodeParent = listNode.getParentOrThrow(); - const isIndented = $isListItemNode(listNodeParent); + const listNodeParent = listNode.getParent(); + + if (!$isListNode(listNode)) { + return false; + } if (listNode.getChildrenSize() === 1) { - if (isIndented) { - // if the list node is nested, we just want to remove it, - // effectively unindenting it. + if ($isListItemNode(listNodeParent)) { listNode.remove(); listNodeParent.select(); } else { listNode.insertBefore(paragraph); listNode.remove(); - // If we have selection on the list item, we'll need to move it - // to the paragraph - const anchor = selection.anchor; - const focus = selection.focus; - const key = paragraph.getKey(); - - if (anchor.type === 'element' && anchor.getNode().is(this)) { - anchor.set(key, anchor.offset, 'element'); - } - - if (focus.type === 'element' && focus.getNode().is(this)) { - focus.set(key, focus.offset, 'element'); - } + paragraph.select(); } - } else { - listNode.insertBefore(paragraph); - this.remove(); } return true; diff --git a/packages/lexical-list/src/formatList.ts b/packages/lexical-list/src/formatList.ts index 3dc4a22ea20..eb2d06538fe 100644 --- a/packages/lexical-list/src/formatList.ts +++ b/packages/lexical-list/src/formatList.ts @@ -242,10 +242,12 @@ export function removeList(editor: LexicalEditor): void { const node = nodes[i]; if ($isLeafNode(node)) { - const listItemNode = $getNearestNodeOfType(node, ListItemNode); + if ($isListItemNode(node)) { + const listItemNode = $getNearestNodeOfType(node, ListItemNode); - if (listItemNode != null) { - listNodes.add($getTopListNode(listItemNode)); + if (listItemNode != null) { + listNodes.add($getTopListNode(listItemNode)); + } } } } @@ -478,12 +480,14 @@ export function $handleListInsertParagraph(): boolean { if (!$isRangeSelection(selection) || !selection.isCollapsed()) { return false; } - // Only run this code on empty list items + //Only run this code on empty list items + const anchor = selection.anchor.getNode(); if (!$isListItemNode(anchor) || anchor.getChildrenSize() !== 0) { return false; } + const topListNode = $getTopListNode(anchor); const parent = anchor.getParent(); @@ -493,8 +497,7 @@ export function $handleListInsertParagraph(): boolean { ); const grandparent = parent.getParent(); - - let replacementNode; + let replacementNode: ElementNode; if ($isRootOrShadowRoot(grandparent)) { replacementNode = $createParagraphNode(); @@ -505,13 +508,12 @@ export function $handleListInsertParagraph(): boolean { } else { return false; } + replacementNode.select(); const nextSiblings = anchor.getNextSiblings(); - if (nextSiblings.length > 0) { const newList = $createListNode(parent.getListType()); - if ($isParagraphNode(replacementNode)) { replacementNode.insertAfter(newList); } else { @@ -524,9 +526,7 @@ export function $handleListInsertParagraph(): boolean { newList.append(sibling); }); } - - // Don't leave hanging nested empty lists + //Don't leave hanging nested empty lists $removeHighestEmptyListParent(anchor); - return true; } From 6b7d0c745924390a58c1473bd38ba4f66b811092 Mon Sep 17 00:00:00 2001 From: "C." Date: Thu, 5 Dec 2024 12:48:00 -0600 Subject: [PATCH 07/23] remove textformat from list utils --- packages/lexical-list/src/utils.ts | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/lexical-list/src/utils.ts b/packages/lexical-list/src/utils.ts index e0d6e149cb7..9b443f76205 100644 --- a/packages/lexical-list/src/utils.ts +++ b/packages/lexical-list/src/utils.ts @@ -7,7 +7,7 @@ */ import {$findMatchingParent} from '@lexical/utils'; -import {type LexicalNode, type Spread, TEXT_TYPE_TO_FORMAT} from 'lexical'; +import {type LexicalNode, type Spread} from 'lexical'; import invariant from 'shared/invariant'; import { @@ -202,24 +202,3 @@ export function $wrapInListItem(node: LexicalNode): ListItemNode { const listItemWrapper = $createListItemNode(); return listItemWrapper.append(node); } - -export function toggleTextFormatType( - format: number, - type: TextFormatType, - alignWithFormat: null | number, -): number { - const activeFormat = TEXT_TYPE_TO_FORMAT[type]; - if ( - alignWithFormat !== null && - (format & activeFormat) === (alignWithFormat & activeFormat) - ) { - return format; - } - let newFormat = format ^ activeFormat; - if (type === 'subscript') { - newFormat &= ~TEXT_TYPE_TO_FORMAT.superscript; - } else if (type === 'superscript') { - newFormat &= ~TEXT_TYPE_TO_FORMAT.subscript; - } - return newFormat; -} From e381faeda6b70e887402c567c3a50ddfdad0379d Mon Sep 17 00:00:00 2001 From: "C." Date: Thu, 5 Dec 2024 12:51:20 -0600 Subject: [PATCH 08/23] Remove getformatflags method --- packages/lexical-list/src/LexicalListItemNode.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index c4f176de743..95466c5b168 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -15,6 +15,7 @@ import { import { $applyNodeReplacement, $createParagraphNode, + $getSelection, $isElementNode, $isParagraphNode, $isRangeSelection, @@ -40,7 +41,7 @@ import normalizeClassNames from 'shared/normalizeClassNames'; import {$createListNode, $isListNode} from './'; import {$handleIndent, $handleOutdent, mergeLists} from './formatList'; -import {isNestedListNode, toggleTextFormatType} from './utils'; +import {isNestedListNode} from './utils'; export type SerializedListItemNode = Spread< { @@ -89,12 +90,6 @@ export class ListItemNode extends ParagraphNode { return (this.getTextFormat() & formatFlag) !== 0; } - getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number { - const self = this.getLatest(); - const format = self.__textFormat; - return toggleTextFormatType(format, type, alignWithFormat); - } - createDOM(config: EditorConfig): HTMLElement { const element = document.createElement('li'); const parent = this.getParent(); From 815a7c99657674baf996fead026d6c7e8c1e8ef7 Mon Sep 17 00:00:00 2001 From: "C." Date: Thu, 5 Dec 2024 13:06:36 -0600 Subject: [PATCH 09/23] update lexicalserialization with listitemnode changes --- .../lexical/src/__tests__/unit/LexicalSerialization.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts b/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts index 9237bc9d3dd..96820722dcd 100644 --- a/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts +++ b/packages/lexical/src/__tests__/unit/LexicalSerialization.test.ts @@ -110,7 +110,7 @@ describe('LexicalSerialization tests', () => { }); const stringifiedEditorState = JSON.stringify(editor.getEditorState()); - const expectedStringifiedEditorState = `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1}],"direction":"ltr","format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`; + const expectedStringifiedEditorState = `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":"ltr","format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":"ltr","format":"","indent":0,"type":"tablerow","version":1}],"direction":"ltr","format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`; expect(stringifiedEditorState).toBe(expectedStringifiedEditorState); @@ -119,7 +119,7 @@ describe('LexicalSerialization tests', () => { const otherStringifiedEditorState = JSON.stringify(editorState); expect(otherStringifiedEditorState).toBe( - `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1}],"direction":null,"format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`, + `{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Welcome to the playground","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"heading","version":1,"tag":"h1"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"quote","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"The playground is a demo environment built with ","type":"text","version":1},{"detail":0,"format":16,"mode":"normal","style":"","text":"@lexical/react","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":". Try typing in ","type":"text","version":1},{"detail":0,"format":1,"mode":"normal","style":"","text":"some text","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" with ","type":"text","version":1},{"detail":0,"format":2,"mode":"normal","style":"","text":"different","type":"text","version":1},{"detail":0,"format":0,"mode":"normal","style":"","text":" formats.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"If you'd like to find out more about Lexical, you can:","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Visit the ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lexical website","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://lexical.dev/"},{"detail":0,"format":0,"mode":"normal","style":"","text":" for documentation and more information.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Check out the code on our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"GitHub repository","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":2},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Playground code can be found ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"here","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://github.com/facebook/lexical/tree/main/packages/lexical-playground"},{"detail":0,"format":0,"mode":"normal","style":"","text":".","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":3},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Join our ","type":"text","version":1},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Discord Server","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"link","version":1,"rel":null,"target":null,"title":null,"url":"https://discord.com/invite/KmG4wQnnD9"},{"detail":0,"format":0,"mode":"normal","style":"","text":" and chat with the team.","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"listitem","version":1,"textFormat":0,"textStyle":"","value":4}],"direction":"ltr","format":"","indent":0,"type":"list","version":1,"listType":"bullet","start":1,"tag":"ul"},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""},{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"const lexical = \\"awesome\\"","type":"code-highlight","version":1}],"direction":"ltr","format":"","indent":0,"type":"code","version":1,"language":"javascript"},{"children":[{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":3,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":1,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1},{"children":[{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":2,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1},{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"tablecell","version":1,"backgroundColor":null,"colSpan":1,"headerState":0,"rowSpan":1}],"direction":null,"format":"","indent":0,"type":"tablerow","version":1}],"direction":null,"format":"","indent":0,"type":"table","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}`, ); }); }); From 4472c72f8bff38785564dd36f3bb589f55439790 Mon Sep 17 00:00:00 2001 From: "C." <106287207+citruscai@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:49:00 -0600 Subject: [PATCH 10/23] Update packages/lexical-list/src/LexicalListItemNode.ts Co-authored-by: Bob Ippolito --- packages/lexical-list/src/LexicalListItemNode.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 95466c5b168..d50439fbc6e 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -58,7 +58,6 @@ export class ListItemNode extends ParagraphNode { __value: number; /** @internal */ __checked?: boolean; - __textFormat: number; static getType(): string { return 'listitem'; From 249a7e4155a27ba6af3867e3c91babb9911f4210 Mon Sep 17 00:00:00 2001 From: "C." <106287207+citruscai@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:50:32 -0600 Subject: [PATCH 11/23] Update packages/lexical-list/src/LexicalListItemNode.ts Co-authored-by: Bob Ippolito --- packages/lexical-list/src/LexicalListItemNode.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index d50439fbc6e..2677b9090fe 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -47,7 +47,8 @@ export type SerializedListItemNode = Spread< { checked: boolean | undefined; value: number; - textFormat: number; + textFormat?: number; + textStyle?: string; }, SerializedParagraphNode >; From ea7b3da65ffbb34196da691ec4213017505a3bb1 Mon Sep 17 00:00:00 2001 From: "C." <106287207+citruscai@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:50:49 -0600 Subject: [PATCH 12/23] Update packages/lexical-list/src/LexicalListItemNode.ts Co-authored-by: Bob Ippolito --- packages/lexical-list/src/LexicalListItemNode.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 2677b9090fe..684fcb915d3 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -146,7 +146,12 @@ export class ListItemNode extends ParagraphNode { node.setValue(serializedNode.value); node.setFormat(serializedNode.format); node.setDirection(serializedNode.direction); - node.setTextFormat(serializedNode.textFormat); + if (typeof serializdNode.textFormat === 'number') { + node.setTextFormat(serializedNode.textFormat); + } + if (typeof serializdNode.textStyle === 'string') { + node.setTextStyle(serializedNode.textStyle); + } return node; } From 975765ddd0201bb97db244686f8b2fa3c3a19c92 Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 13:22:11 -0600 Subject: [PATCH 13/23] ensure backwards compatiability with Lexical list item node and paragraph node --- .../lexical-list/src/LexicalListItemNode.ts | 21 +++++++++---------- .../lexical/src/nodes/LexicalParagraphNode.ts | 16 ++++++-------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 684fcb915d3..c2cafe7a7d6 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -47,8 +47,8 @@ export type SerializedListItemNode = Spread< { checked: boolean | undefined; value: number; - textFormat?: number; - textStyle?: string; + textFormat?: number | undefined; + textStyle?: string | undefined; }, SerializedParagraphNode >; @@ -100,11 +100,11 @@ export class ListItemNode extends ParagraphNode { $setListItemThemeClassNames(element, config.theme, this); return element; } - updateDOM( - prevNode: ParagraphNode, - dom: HTMLElement, - config: EditorConfig, - ): boolean { + updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean { + if (super.updateDOM(prevNode, dom, config)) { + return true; + } + const parent = this.getParent(); if ($isListNode(parent) && parent.getListType() === 'check') { const listItemNode = $isListItemNode(prevNode) ? prevNode : null; @@ -113,7 +113,7 @@ export class ListItemNode extends ParagraphNode { // @ts-expect-error - this is always HTMLListItemElement dom.value = this.__value; $setListItemThemeClassNames(dom, config.theme, this); - return super.updateDOM(prevNode, dom, config); + return false; } static transform(): (node: LexicalNode) => void { @@ -146,10 +146,10 @@ export class ListItemNode extends ParagraphNode { node.setValue(serializedNode.value); node.setFormat(serializedNode.format); node.setDirection(serializedNode.direction); - if (typeof serializdNode.textFormat === 'number') { + if (typeof serializedNode.textFormat === 'number') { node.setTextFormat(serializedNode.textFormat); } - if (typeof serializdNode.textStyle === 'string') { + if (typeof serializedNode.textStyle === 'string') { node.setTextStyle(serializedNode.textStyle); } return node; @@ -167,7 +167,6 @@ export class ListItemNode extends ParagraphNode { return { ...super.exportJSON(), checked: this.getChecked(), - textFormat: this.getTextFormat(), type: 'listitem', value: this.getValue(), version: 1, diff --git a/packages/lexical/src/nodes/LexicalParagraphNode.ts b/packages/lexical/src/nodes/LexicalParagraphNode.ts index c1250aeae16..9b6c187937e 100644 --- a/packages/lexical/src/nodes/LexicalParagraphNode.ts +++ b/packages/lexical/src/nodes/LexicalParagraphNode.ts @@ -38,8 +38,8 @@ import {$isTextNode, TextFormatType} from './LexicalTextNode'; export type SerializedParagraphNode = Spread< { - textFormat: number; - textStyle: string; + textFormat?: number; + textStyle?: string; }, SerializedElementNode >; @@ -48,8 +48,8 @@ export type SerializedParagraphNode = Spread< export class ParagraphNode extends ElementNode { ['constructor']!: KlassConstructor; /** @internal */ - __textFormat: number; - __textStyle: string; + __textFormat?: number | undefined; + __textStyle?: string | undefined; constructor(key?: NodeKey) { super(key); @@ -61,7 +61,7 @@ export class ParagraphNode extends ElementNode { return 'paragraph'; } - getTextFormat(): number { + getTextFormat(): number | undefined { const self = this.getLatest(); return self.__textFormat; } @@ -120,11 +120,7 @@ export class ParagraphNode extends ElementNode { } return dom; } - updateDOM( - prevNode: ParagraphNode, - dom: HTMLElement, - config: EditorConfig, - ): boolean { + updateDOM(prevNode: this, dom: HTMLElement, config: EditorConfig): boolean { return false; } From 211963a3ad873d45c96e61a72189c63136399493 Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 17:34:08 -0600 Subject: [PATCH 14/23] Resolve type errros after changes to paragraphnode --- .../lexical-list/src/LexicalListItemNode.ts | 17 ---------- .../src/LexicalTableSelection.ts | 2 +- packages/lexical/src/LexicalEvents.ts | 4 +-- .../lexical/src/nodes/LexicalParagraphNode.ts | 34 +++++++++++++------ 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index c2cafe7a7d6..99de8e1caf1 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -33,8 +33,6 @@ import { RangeSelection, SerializedParagraphNode, Spread, - TEXT_TYPE_TO_FORMAT, - TextFormatType, } from 'lexical'; import invariant from 'shared/invariant'; import normalizeClassNames from 'shared/normalizeClassNames'; @@ -74,21 +72,6 @@ export class ListItemNode extends ParagraphNode { this.__checked = checked; this.__textFormat = 0; } - getTextFormat(): number { - const self = this.getLatest(); - return self.__textFormat; - } - - setTextFormat(type: number): this { - const self = this.getWritable(); - self.__textFormat = type; - return self; - } - - hasTextFormat(type: TextFormatType): boolean { - const formatFlag = TEXT_TYPE_TO_FORMAT[type]; - return (this.getTextFormat() & formatFlag) !== 0; - } createDOM(config: EditorConfig): HTMLElement { const element = document.createElement('li'); diff --git a/packages/lexical-table/src/LexicalTableSelection.ts b/packages/lexical-table/src/LexicalTableSelection.ts index 185d95100f3..48afccba80a 100644 --- a/packages/lexical-table/src/LexicalTableSelection.ts +++ b/packages/lexical-table/src/LexicalTableSelection.ts @@ -220,7 +220,7 @@ export class TableSelection implements BaseSelection { cellNodes.forEach((cellNode: TableCellNode) => { const paragraph = cellNode.getFirstChild(); if ($isParagraphNode(paragraph)) { - format |= paragraph.getTextFormat(); + format |= paragraph.getTextFormat() ?? 0; } }); diff --git a/packages/lexical/src/LexicalEvents.ts b/packages/lexical/src/LexicalEvents.ts index 663fbb236a4..eb0d87358e9 100644 --- a/packages/lexical/src/LexicalEvents.ts +++ b/packages/lexical/src/LexicalEvents.ts @@ -354,8 +354,8 @@ function onSelectionChange( lastNode instanceof ParagraphNode && lastNode.getChildrenSize() === 0 ) { - selection.format = lastNode.getTextFormat(); - selection.style = lastNode.getTextStyle(); + selection.format = lastNode.getTextFormat() ?? 0; + selection.style = lastNode.getTextStyle() ?? ''; } else { selection.format = 0; } diff --git a/packages/lexical/src/nodes/LexicalParagraphNode.ts b/packages/lexical/src/nodes/LexicalParagraphNode.ts index 9b6c187937e..9c45d7f0b74 100644 --- a/packages/lexical/src/nodes/LexicalParagraphNode.ts +++ b/packages/lexical/src/nodes/LexicalParagraphNode.ts @@ -38,8 +38,8 @@ import {$isTextNode, TextFormatType} from './LexicalTextNode'; export type SerializedParagraphNode = Spread< { - textFormat?: number; - textStyle?: string; + textFormat?: number | undefined; + textStyle?: string | undefined; }, SerializedElementNode >; @@ -61,9 +61,9 @@ export class ParagraphNode extends ElementNode { return 'paragraph'; } - getTextFormat(): number | undefined { + getTextFormat(): number { const self = this.getLatest(); - return self.__textFormat; + return self.__textFormat ?? 0; } setTextFormat(type: number): this { @@ -74,7 +74,13 @@ export class ParagraphNode extends ElementNode { hasTextFormat(type: TextFormatType): boolean { const formatFlag = TEXT_TYPE_TO_FORMAT[type]; - return (this.getTextFormat() & formatFlag) !== 0; + const textFormat = this.getTextFormat() ?? 0; + + if (textFormat === 0) { + return false; + } + + return (textFormat & formatFlag) !== 0; } /** @@ -82,13 +88,16 @@ export class ParagraphNode extends ElementNode { * * @returns a number representing the TextFormatTypes applied to the node. */ - getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number { + getFormatFlags( + type: TextFormatType, + alignWithFormat: null | number, + ): number | undefined { const self = this.getLatest(); - const format = self.__textFormat; + const format = self.__textFormat ?? 0; return toggleTextFormatType(format, type, alignWithFormat); } - getTextStyle(): string { + getTextStyle(): string | undefined { const self = this.getLatest(); return self.__textStyle; } @@ -160,7 +169,9 @@ export class ParagraphNode extends ElementNode { node.setFormat(serializedNode.format); node.setIndent(serializedNode.indent); node.setDirection(serializedNode.direction); - node.setTextFormat(serializedNode.textFormat); + if (typeof serializedNode.textFormat === 'number') { + node.setTextFormat(serializedNode.textFormat); + } return node; } @@ -186,7 +197,10 @@ export class ParagraphNode extends ElementNode { const direction = this.getDirection(); newElement.setDirection(direction); newElement.setFormat(this.getFormatType()); - newElement.setStyle(this.getTextStyle()); + if (typeof this.getStyle() === 'string') { + newElement.setStyle(this.getStyle()); + } + this.insertAfter(newElement, restoreSelection); return newElement; } From 6fc604b3263bf237c37a4f04297b4b312dc8d604 Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 17:46:42 -0600 Subject: [PATCH 15/23] integrity checks more --- packages/lexical/src/nodes/LexicalParagraphNode.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/lexical/src/nodes/LexicalParagraphNode.ts b/packages/lexical/src/nodes/LexicalParagraphNode.ts index 9c45d7f0b74..b3d7c989e21 100644 --- a/packages/lexical/src/nodes/LexicalParagraphNode.ts +++ b/packages/lexical/src/nodes/LexicalParagraphNode.ts @@ -88,13 +88,10 @@ export class ParagraphNode extends ElementNode { * * @returns a number representing the TextFormatTypes applied to the node. */ - getFormatFlags( - type: TextFormatType, - alignWithFormat: null | number, - ): number | undefined { + getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number { const self = this.getLatest(); const format = self.__textFormat ?? 0; - return toggleTextFormatType(format, type, alignWithFormat); + return toggleTextFormatType(format, type, alignWithFormat) ?? 0; } getTextStyle(): string | undefined { From bb0874736f804d306588fef74828045308f08d26 Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 17:58:36 -0600 Subject: [PATCH 16/23] trying to fix integrity errors :( --- .../lexical-list/src/LexicalListItemNode.ts | 3 --- .../src/LexicalTableSelection.ts | 2 +- .../lexical/src/nodes/LexicalParagraphNode.ts | 20 +++++++++---------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 99de8e1caf1..291b0818549 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -45,8 +45,6 @@ export type SerializedListItemNode = Spread< { checked: boolean | undefined; value: number; - textFormat?: number | undefined; - textStyle?: string | undefined; }, SerializedParagraphNode >; @@ -70,7 +68,6 @@ export class ListItemNode extends ParagraphNode { super(key); this.__value = value === undefined ? 1 : value; this.__checked = checked; - this.__textFormat = 0; } createDOM(config: EditorConfig): HTMLElement { diff --git a/packages/lexical-table/src/LexicalTableSelection.ts b/packages/lexical-table/src/LexicalTableSelection.ts index 48afccba80a..e36070caa5c 100644 --- a/packages/lexical-table/src/LexicalTableSelection.ts +++ b/packages/lexical-table/src/LexicalTableSelection.ts @@ -220,7 +220,7 @@ export class TableSelection implements BaseSelection { cellNodes.forEach((cellNode: TableCellNode) => { const paragraph = cellNode.getFirstChild(); if ($isParagraphNode(paragraph)) { - format |= paragraph.getTextFormat() ?? 0; + format |= paragraph.getTextFormat() || 0; } }); diff --git a/packages/lexical/src/nodes/LexicalParagraphNode.ts b/packages/lexical/src/nodes/LexicalParagraphNode.ts index b3d7c989e21..5922043e881 100644 --- a/packages/lexical/src/nodes/LexicalParagraphNode.ts +++ b/packages/lexical/src/nodes/LexicalParagraphNode.ts @@ -38,8 +38,8 @@ import {$isTextNode, TextFormatType} from './LexicalTextNode'; export type SerializedParagraphNode = Spread< { - textFormat?: number | undefined; - textStyle?: string | undefined; + textFormat?: number; + textStyle?: string; }, SerializedElementNode >; @@ -48,8 +48,8 @@ export type SerializedParagraphNode = Spread< export class ParagraphNode extends ElementNode { ['constructor']!: KlassConstructor; /** @internal */ - __textFormat?: number | undefined; - __textStyle?: string | undefined; + __textFormat?: number; + __textStyle?: string; constructor(key?: NodeKey) { super(key); @@ -63,7 +63,7 @@ export class ParagraphNode extends ElementNode { getTextFormat(): number { const self = this.getLatest(); - return self.__textFormat ?? 0; + return self.__textFormat || 0; } setTextFormat(type: number): this { @@ -74,7 +74,7 @@ export class ParagraphNode extends ElementNode { hasTextFormat(type: TextFormatType): boolean { const formatFlag = TEXT_TYPE_TO_FORMAT[type]; - const textFormat = this.getTextFormat() ?? 0; + const textFormat = this.getTextFormat() || 0; if (textFormat === 0) { return false; @@ -90,13 +90,13 @@ export class ParagraphNode extends ElementNode { */ getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number { const self = this.getLatest(); - const format = self.__textFormat ?? 0; - return toggleTextFormatType(format, type, alignWithFormat) ?? 0; + const format = self.__textFormat; + return toggleTextFormatType(format || 0, type, alignWithFormat); } - getTextStyle(): string | undefined { + getTextStyle(): string { const self = this.getLatest(); - return self.__textStyle; + return self.__textStyle || ''; } setTextStyle(style: string): this { From 8e0534aea5ab7a4192402d702de8262cfa09d84d Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 18:02:47 -0600 Subject: [PATCH 17/23] more integrity errors fix :( --- packages/lexical/src/LexicalEvents.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lexical/src/LexicalEvents.ts b/packages/lexical/src/LexicalEvents.ts index eb0d87358e9..841cd5a738c 100644 --- a/packages/lexical/src/LexicalEvents.ts +++ b/packages/lexical/src/LexicalEvents.ts @@ -354,8 +354,8 @@ function onSelectionChange( lastNode instanceof ParagraphNode && lastNode.getChildrenSize() === 0 ) { - selection.format = lastNode.getTextFormat() ?? 0; - selection.style = lastNode.getTextStyle() ?? ''; + selection.format = lastNode.getTextFormat() || 0; + selection.style = lastNode.getTextStyle() || ''; } else { selection.format = 0; } From 0bea28caa438214b12a3db596cad6af8441ae197 Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 18:14:45 -0600 Subject: [PATCH 18/23] adjust unit test for LexicalSelection --- packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx b/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx index 3986f27806f..30f72705040 100644 --- a/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx +++ b/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx @@ -1026,7 +1026,7 @@ describe('LexicalEditor tests', () => { editable ? 'editable' : 'non-editable' })`, async () => { const JSON_EDITOR_STATE = - '{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"123","type":"text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'; + '{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"123","type":"text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,textStyle":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'; init(); const contentEditable = editor.getRootElement(); editor.setEditable(editable); From bdc7f340baba001010286374e2cab960f1bc4eb7 Mon Sep 17 00:00:00 2001 From: "C." Date: Fri, 6 Dec 2024 18:28:15 -0600 Subject: [PATCH 19/23] unit fix attempt 2.0 jesus christ help me please --- packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx b/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx index 30f72705040..7f7be6d9c5f 100644 --- a/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx +++ b/packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx @@ -1026,7 +1026,7 @@ describe('LexicalEditor tests', () => { editable ? 'editable' : 'non-editable' })`, async () => { const JSON_EDITOR_STATE = - '{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"123","type":"text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,textStyle":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'; + '{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"123","type":"text","version":1}],"direction":null,"format":"","indent":0,"type":"paragraph","version":1,"textFormat":0,"textStyle":""}],"direction":null,"format":"","indent":0,"type":"root","version":1}}'; init(); const contentEditable = editor.getRootElement(); editor.setEditable(editable); From b969c97b1eeb65c8b150c29445de8d22986d29c9 Mon Sep 17 00:00:00 2001 From: "C." <106287207+citruscai@users.noreply.github.com> Date: Sat, 7 Dec 2024 20:48:55 -0600 Subject: [PATCH 20/23] Apply suggestions from code review Co-authored-by: Bob Ippolito --- .../lexical-list/src/LexicalListItemNode.ts | 3 +-- packages/lexical-list/src/formatList.ts | 4 +-- .../src/LexicalTableSelection.ts | 2 +- packages/lexical/src/LexicalEvents.ts | 4 +-- .../lexical/src/nodes/LexicalParagraphNode.ts | 26 +++++++------------ 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/packages/lexical-list/src/LexicalListItemNode.ts b/packages/lexical-list/src/LexicalListItemNode.ts index 291b0818549..5581aba730b 100644 --- a/packages/lexical-list/src/LexicalListItemNode.ts +++ b/packages/lexical-list/src/LexicalListItemNode.ts @@ -87,8 +87,7 @@ export class ListItemNode extends ParagraphNode { const parent = this.getParent(); if ($isListNode(parent) && parent.getListType() === 'check') { - const listItemNode = $isListItemNode(prevNode) ? prevNode : null; - updateListItemChecked(dom, this, listItemNode, parent); + updateListItemChecked(dom, this, prevNode, parent); } // @ts-expect-error - this is always HTMLListItemElement dom.value = this.__value; diff --git a/packages/lexical-list/src/formatList.ts b/packages/lexical-list/src/formatList.ts index eb2d06538fe..0feed6f2cfb 100644 --- a/packages/lexical-list/src/formatList.ts +++ b/packages/lexical-list/src/formatList.ts @@ -480,7 +480,7 @@ export function $handleListInsertParagraph(): boolean { if (!$isRangeSelection(selection) || !selection.isCollapsed()) { return false; } - //Only run this code on empty list items + // Only run this code on empty list items const anchor = selection.anchor.getNode(); @@ -526,7 +526,7 @@ export function $handleListInsertParagraph(): boolean { newList.append(sibling); }); } - //Don't leave hanging nested empty lists + // Don't leave hanging nested empty lists $removeHighestEmptyListParent(anchor); return true; } diff --git a/packages/lexical-table/src/LexicalTableSelection.ts b/packages/lexical-table/src/LexicalTableSelection.ts index e36070caa5c..185d95100f3 100644 --- a/packages/lexical-table/src/LexicalTableSelection.ts +++ b/packages/lexical-table/src/LexicalTableSelection.ts @@ -220,7 +220,7 @@ export class TableSelection implements BaseSelection { cellNodes.forEach((cellNode: TableCellNode) => { const paragraph = cellNode.getFirstChild(); if ($isParagraphNode(paragraph)) { - format |= paragraph.getTextFormat() || 0; + format |= paragraph.getTextFormat(); } }); diff --git a/packages/lexical/src/LexicalEvents.ts b/packages/lexical/src/LexicalEvents.ts index 841cd5a738c..663fbb236a4 100644 --- a/packages/lexical/src/LexicalEvents.ts +++ b/packages/lexical/src/LexicalEvents.ts @@ -354,8 +354,8 @@ function onSelectionChange( lastNode instanceof ParagraphNode && lastNode.getChildrenSize() === 0 ) { - selection.format = lastNode.getTextFormat() || 0; - selection.style = lastNode.getTextStyle() || ''; + selection.format = lastNode.getTextFormat(); + selection.style = lastNode.getTextStyle(); } else { selection.format = 0; } diff --git a/packages/lexical/src/nodes/LexicalParagraphNode.ts b/packages/lexical/src/nodes/LexicalParagraphNode.ts index 5922043e881..6bef34f6271 100644 --- a/packages/lexical/src/nodes/LexicalParagraphNode.ts +++ b/packages/lexical/src/nodes/LexicalParagraphNode.ts @@ -38,8 +38,8 @@ import {$isTextNode, TextFormatType} from './LexicalTextNode'; export type SerializedParagraphNode = Spread< { - textFormat?: number; - textStyle?: string; + textFormat: number; + textStyle: string; }, SerializedElementNode >; @@ -48,8 +48,8 @@ export type SerializedParagraphNode = Spread< export class ParagraphNode extends ElementNode { ['constructor']!: KlassConstructor; /** @internal */ - __textFormat?: number; - __textStyle?: string; + __textFormat: number; + __textStyle: string; constructor(key?: NodeKey) { super(key); @@ -63,7 +63,7 @@ export class ParagraphNode extends ElementNode { getTextFormat(): number { const self = this.getLatest(); - return self.__textFormat || 0; + return self.__textFormat; } setTextFormat(type: number): this { @@ -74,13 +74,7 @@ export class ParagraphNode extends ElementNode { hasTextFormat(type: TextFormatType): boolean { const formatFlag = TEXT_TYPE_TO_FORMAT[type]; - const textFormat = this.getTextFormat() || 0; - - if (textFormat === 0) { - return false; - } - - return (textFormat & formatFlag) !== 0; +return (this.getTextFormat() & formatFlag) !== 0; } /** @@ -91,12 +85,12 @@ export class ParagraphNode extends ElementNode { getFormatFlags(type: TextFormatType, alignWithFormat: null | number): number { const self = this.getLatest(); const format = self.__textFormat; - return toggleTextFormatType(format || 0, type, alignWithFormat); + return toggleTextFormatType(format, type, alignWithFormat); } getTextStyle(): string { const self = this.getLatest(); - return self.__textStyle || ''; + return self.__textStyle; } setTextStyle(style: string): this { @@ -194,9 +188,7 @@ export class ParagraphNode extends ElementNode { const direction = this.getDirection(); newElement.setDirection(direction); newElement.setFormat(this.getFormatType()); - if (typeof this.getStyle() === 'string') { - newElement.setStyle(this.getStyle()); - } + newElement.setStyle(this.getStyle()); this.insertAfter(newElement, restoreSelection); return newElement; From 670022cfadecc8c0b875f407a1e84f797cf30d95 Mon Sep 17 00:00:00 2001 From: "C." Date: Sat, 7 Dec 2024 20:58:20 -0600 Subject: [PATCH 21/23] Empty-Commit From 905ccbbbef00e3cd564514d2a2c37cc27cb9108f Mon Sep 17 00:00:00 2001 From: "C." Date: Sun, 8 Dec 2024 11:01:54 -0600 Subject: [PATCH 22/23] Run prettier:fix --- packages/lexical/src/nodes/LexicalParagraphNode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lexical/src/nodes/LexicalParagraphNode.ts b/packages/lexical/src/nodes/LexicalParagraphNode.ts index 6bef34f6271..036799b3b71 100644 --- a/packages/lexical/src/nodes/LexicalParagraphNode.ts +++ b/packages/lexical/src/nodes/LexicalParagraphNode.ts @@ -74,7 +74,7 @@ export class ParagraphNode extends ElementNode { hasTextFormat(type: TextFormatType): boolean { const formatFlag = TEXT_TYPE_TO_FORMAT[type]; -return (this.getTextFormat() & formatFlag) !== 0; + return (this.getTextFormat() & formatFlag) !== 0; } /** From 8dd65a1372244187a27688429a63628afae5b221 Mon Sep 17 00:00:00 2001 From: "C." Date: Mon, 9 Dec 2024 13:49:31 -0600 Subject: [PATCH 23/23] Revert changes in insertList method in formatList --- packages/lexical-list/src/formatList.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/lexical-list/src/formatList.ts b/packages/lexical-list/src/formatList.ts index 0feed6f2cfb..83172275908 100644 --- a/packages/lexical-list/src/formatList.ts +++ b/packages/lexical-list/src/formatList.ts @@ -242,12 +242,9 @@ export function removeList(editor: LexicalEditor): void { const node = nodes[i]; if ($isLeafNode(node)) { - if ($isListItemNode(node)) { - const listItemNode = $getNearestNodeOfType(node, ListItemNode); - - if (listItemNode != null) { - listNodes.add($getTopListNode(listItemNode)); - } + const listItemNode = $getNearestNodeOfType(node, ListItemNode); + if (listItemNode != null) { + listNodes.add($getTopListNode(listItemNode)); } } }