Skip to content

[lexical-react] Feat: add draggable block support to individual list items#8178

Open
Sa-Te wants to merge 1 commit intofacebook:mainfrom
Sa-Te:feature/draggable-list-items
Open

[lexical-react] Feat: add draggable block support to individual list items#8178
Sa-Te wants to merge 1 commit intofacebook:mainfrom
Sa-Te:feature/draggable-list-items

Conversation

@Sa-Te
Copy link
Contributor

@Sa-Te Sa-Te commented Feb 26, 2026

Description

This PR addresses #7630 by enhancing the DraggableBlockPlugin to support dragging individual ListItemNodes independently of their parent ListNode.

Previously, hovering over a list would only target the top-level <ul> or <ol>, making it impossible to restructure individual items via drag-and-drop.

Key Changes:

  • Recursive Drill-Down: Updated getBlockElement to check if a top-level block is a List. If so, it searches its children to attach the drag handle to the specific <li> being hovered.
  • Gap & Zoom Safety: Expanded the horizontal bounding box for nested items so the drag handle doesn't vanish when the mouse moves left to grab it, while strictly respecting the existing CSS calculateZoomLevel logic.
  • Smart Insertion Logic: Updated $onDrop to handle cross-node drops. If an <li> is dragged out of a list and dropped onto a <p>, it automatically wraps the item in a new ListNode matching the original list type (bullet, number, etc.) to prevent orphaned nodes and aggressive normalization errors.

Impact

Brings the Lexical Playground's restructuring UX to parity with editors like Notion and PlateJS, allowing users to easily reorder list items or break them out into new lists.

Testing

  • Added E2E test: List item one can be successfully dragged below list item two
  • Added E2E test: List item can be dragged out of a list and dropped below a paragraph
  • Verified existing E2E tests for paragraph dragging and edge cases continue to pass.
  • Manually verified in headed mode and verified CSS zoom still functions correctly.

Closes #7630

@vercel
Copy link

vercel bot commented Feb 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview, Comment Feb 27, 2026 8:29pm
lexical-playground Ready Ready Preview, Comment Feb 27, 2026 8:29pm

Request Review

Copy link
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UX here is a bit weird, since it adds new target locations between lists but they don't actually work unless the dragged node is a list. The lines should only show up when the dragged node can be dropped there, so either code should be added to split lists if a paragraph is dragged into it, or the targeting code should be aware of the dragged node's type.

Screen.Recording.2026-02-26.at.13.19.36.mov

@etrepum
Copy link
Collaborator

etrepum commented Feb 26, 2026

The nested list case is also unintuitive, it seems that you can only reorder the top-level

@Sa-Te
Copy link
Contributor Author

Sa-Te commented Feb 27, 2026

The nested list case is also unintuitive, it seems that you can only reorder the top-level

I've just pushed an update that addresses both points:

The Paragraph into List Bug: I added a useRef to track the Lexical key of the node currently being dragged. Now, if the targeting code detects you are dragging a non-list-item (like a Paragraph), it intentionally skips the drill-down logic. This forces the drop line to appear strictly above or below the entire list, preventing the confusing mid-list targeting.

The Nested List Bug: I refactored the targeting helper to be recursive. It now infinitely dives into nested <ul>/<ol> structures so you can seamlessly reorder deeply nested items. (I also updated the E2E tests to explicitly verify this behavior).

Note on the horizontal "indent" drag behavior: I noticed in testing that if you drag a nested item vertically below its parent's bounding box, it natively drops into the outer list (due to Lexical's Y-axis targeting). Implementing X-axis drag-to-indent tracking would require a rewrite of the core coordinate engine, so I kept this PR strictly scoped to enabling the vertical reordering of list items.

@etrepum
Copy link
Collaborator

etrepum commented Feb 28, 2026

I think if we're trying to re-implement something like Notion's list support for draggable block reordering, why not rewrite any necessary code required to make that happen? There isn't really any API surface here to worry about preserving compatibility. The way Notion shows you where the node will land is much more intuitive

@levensta
Copy link
Contributor

levensta commented Feb 28, 2026

Allow me to share my thoughts on this

In fact, it is not necessary to use drag'n'drop only for nested list items. It can be enabled for any nested items inside containers. For example, in lexical-playground, these are Collapsible Container and Columns Layout/Item. I think it's a good idea, at least so that the plugin doesn't depend on specific nodes.

If you look at Notion, there is also a Callout container from which and into which you can drag and drop any elements, while still being able to use drag on the Callout itself.

Notion.s.nested.drag.mov

By the way, I forked the DraggableBlockPlugin to add a nested drag feature to it, but I disabled the ability to drag individual list items because, as Bob said, there is a problem with new fake target locations, as well as the problem that it is impossible to move the entire list at once. The last problem in Notion is solved by selecting several blocks at once and then using the drag icon, but this is a much more complicated feature than simple nested drag.

Notion.s.entire.or.partial.list.drag.mov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🌮 Feature: Draggable Individual List Items 🌮

3 participants