github facebook/lexical v0.28.0

latest release: v0.28.1-nightly.20250319.0
one day ago

Ad-hoc minor release with important bug fixes and some enabling features. The most important fix is in #7341 - where under certain conditions nodes may not be updated in the DOM at all (this bug is rarely triggered and has been around for 3+ years).

Breaking Changes

NodeSelection default delete handler #7323

If you have any $onDelete handlers copied from the playground for KEY_DELETE_COMMAND and KEY_BACKSPACE_COMMAND, you can delete them all now. If you leave them in, it will have similar bugs as prior to this PR.

The default KEY_DELETE_COMMAND and KEY_BACKSPACE_COMMAND handlers now event.preventDefault() and call DELETE_CHARACTER_COMMAND for NodeSelection (in addition to the existing RangeSelection behavior).

The DELETE_CHARACTER_COMMAND handler now handles NodeSelection by calling the new NodeSelection.deleteNodes() method which places a new RangeSelection where the first selected node was (if any, and if it was the current selection) and then removes all of the selected nodes.

RootNode and ListItemNode splice #7341

This PR moves the incorrectly overridden append methods for RootNode and ListNode and moves it to the ElementNode's primitive splice method. For RootNode this means that the exception you'd get for appending a leaf node to the root will be thrown in all situations when that node is inserted, rather than just append. For ListNode this means that the automatic ListItemNode wrapping applies in all situations when children are inserted into the node, not just append.

$insertNodeToNearestRoot changes #7342

Previously $insertNodeToNearestRoot could create empty ElementNode when splitting the current node, now it respects canBeEmpty() and will not split in those cases (e.g. ListNode).

ListItemNode CSS #7325

The approach used in #7024 (since v0.26.0) to have a ListItemNode bullet inherit the style of the first text node was too broad, the inline style on the ListItemNode cascades to all of its children. The only way around this is to change the approach.

To retain this feature, you need to add CSS to your theme to specifically style the marker pseudo-element based on these new custom properties. Here's an example from the playground css:

.PlaygroundEditorTheme__listItem::marker {
  color: var(--listitem-marker-color);
  background-color: var(--listitem-marker-background-color);
  font-family: var(--listitem-marker-font-family);
  font-size: var(--listitem-marker-font-size);
}

TableCellNode importDOM #7318

The importDOM implementation for TableCellNode has been corrected to behave as other Lexical roots do with regard to converting <br> tags to LineBreakNode and wrapping runs of top-level inline nodes (including inline decorators) with ParagraphNode. If you have any specific workarounds accounting for the previous behavior, you can remove them. The one difference from other import paths is that a solitary <br> (e.g. <td><br></td>) is converted to an empty ParagraphNode rather than a ParagraphNode containing a LineBreakNode.

getDOMSlot #7336

If you're using the undocumented internal getDOMSlot API, you may need to change your types. There is now a type parameter for ElementDOMSlot so that the element property can be specialized.

Highlights

Core:

  • 🆕 #7321 Add mutatedNodes to UpdateListener payload
  • 🆕 #7323 Add a default delete handler for NodeSelection - this allowed a lot of boilerplate code to be deleted, and all of the existing implementations were subtly broken
  • #7341 Fix bug in transformer loop that would cause nodes not to get reconciled
  • #7342 Handle canBeEmpty() in $splitNodes
  • 🆕 #7344 Apply RootNode transforms last

List:

  • #7325 Move ListItemNode text style inheritance to CSS custom properties

Tables:

  • #7336 Fix updateDOM for scrollable TableNode
  • #7318 Fix table cell line breaks

React:

  • #7315 Remove unused direct dependencies
  • 🆕 #7338 Add onElementChanged callback to DraggableBlockPlugin

Playground:

  • #7337 Table actions should clear selection instead of moving it to the beginning
  • 🆕 #7338 Add "+" button to DraggableBlockPlugin

Utils:

  • 🆕 #7340 Add a type predicate to objectKlassEquals

New APIs

UpdateListenerPayload mutatedNodes #7321

A mutatedNodes property is now present in the UpdateListener payload. This was done to accommodate the use case when you want to have a MutationListener that listens to all nodes (this is useful in combination with NodeState). Note that this property is only calculated when at least one MutationListener is registered (e.g. editor.registerMutationListener(RootNode, () => {}) is sufficient to compute mutatedNodes for all nodes).

NodeSelection deleteNodes #7323

NodeSelection.deleteNodes() was added to support the default delete handler

RootNode node transform #7344

There is now an ordering guarantee that RootNode node transforms are called last, and documentation about the special case that RootNode is always considered intentionally dirty when any other node is dirty

@lexical/react/LexicalDraggableBlockPlugin onElementChanged #7338

An onElementChanged prop was added to make it possible to implement the "+" button in the playground

What's Changed

  • v0.27.2 by @etrepum in #7314
  • [lexical-react]: Chore: remove unused dependencies from @lexical/react by @AlessioGr in #7315
  • [lexical-editor][Bug fix] Add LexicalEditor.hasNode to flow typing to match typescript by @Zhangerr in #7320
  • [Breaking Change][lexical][lexical-table] Bug Fix: Scrollable TableNode updateDOM fixes and getDOMSlot type refactoring by @etrepum in #7336
  • [lexical] Feature: Add mutatedNodes to UpdateListener payload by @etrepum in #7321
  • [Breaking Change][lexical-list] Bug Fix: Move ListItemNode text style inheritance to custom properties and CSS by @etrepum in #7325
  • [Breaking Change][lexical][lexical-playground] Feature: Add a default delete handler for NodeSelection by @etrepum in #7323
  • [lexical-playground] Table actions should clear selection instead of moving it to the beginning by @etrepum in #7337
  • [lexical-utils] Feature: Add type predicate to objectKlassEquals by @2wheeh in #7340
  • [Breaking Change][lexical] Bug Fix: Fix bug in transformer loop that would cause nodes not to get reconciled by @etrepum in #7341
  • [Breaking Change][lexical-table] Bug Fix: Table cell line breaks behave differently from the intended HTML behavior. by @dineug in #7318
  • [Breaking Change][lexical][lexical-utils]: Bug Fix: Handle canBeEmpty in $splitNodes by @etrepum in #7342
  • [lexical-playground][lexical-react] Feature: Push Draggable Element to Parent by @sescandell in #7338
  • [lexical-website] Docs: Fix broken links to React Rich Collab Example by @etrepum in #7347
  • [lexical] Feature: Apply RootNode transforms last by @etrepum in #7344

New Contributors

Full Changelog: v0.27.2...v0.28.0

Don't miss a new lexical release

NewReleases is sending notifications on new releases.