Skip to content

Commit 38743b4

Browse files
committed
fix(core): address review feedback on paste/parse fixes
- nestedLists: walk siblings via nextSibling so meaningful (non-whitespace) text between bare <li>s prevents them from being merged into one <ul>. Whitespace text nodes still bridge consecutive orphans. - transformPasted: bail out of retypeLeadingParagraphForEmptyTarget during drop events (view.dragging is set), since the slice is inserted at the drop point rather than the current selection.
1 parent 49fe92c commit 38743b4

4 files changed

Lines changed: 35 additions & 5 deletions

File tree

packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`Lift nested lists > Does not merge <li> nodes separated by non-whitespace text 1`] = `"<ul><li>a</li></ul>text<ul><li>b</li></ul>"`;
4+
35
exports[`Lift nested lists > Leaves <li>s already inside a <ul> alone 1`] = `"<ul><li>existing</li></ul><ul><li>orphan</li></ul>"`;
46
57
exports[`Lift nested lists > Lifts multiple bullet lists 1`] = `

packages/core/src/api/parsers/html/util/nestedLists.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ describe("Lift nested lists", () => {
163163
await testHTML(html);
164164
});
165165

166+
it("Does not merge <li> nodes separated by non-whitespace text", async () => {
167+
const html = `<li>a</li>text<li>b</li>`;
168+
await testHTML(html);
169+
});
170+
166171
it("Wraps nested orphan <li>s as inner lists", async () => {
167172
const html = `<li>outer<li>inner</li></li>`;
168173
await testHTML(html);

packages/core/src/api/parsers/html/util/nestedLists.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,26 @@ function wrapOrphanListItems(element: HTMLElement) {
3131
const group: Element[] = [orphan];
3232
handled.add(orphan);
3333

34-
let next = orphan.nextElementSibling;
35-
while (next && next.tagName === "LI" && orphanSet.has(next)) {
36-
group.push(next);
37-
handled.add(next);
38-
next = next.nextElementSibling;
34+
// Walk siblings via nextSibling (not nextElementSibling) so we can stop
35+
// at meaningful text between orphans — only whitespace text is allowed
36+
// to bridge two orphan <li>s into the same <ul>.
37+
let next: Node | null = orphan.nextSibling;
38+
while (next) {
39+
if (isWhitespaceNode(next)) {
40+
next = next.nextSibling;
41+
continue;
42+
}
43+
if (
44+
next.nodeType === 1 &&
45+
(next as Element).tagName === "LI" &&
46+
orphanSet.has(next as Element)
47+
) {
48+
group.push(next as Element);
49+
handled.add(next as Element);
50+
next = next.nextSibling;
51+
continue;
52+
}
53+
break;
3954
}
4055

4156
const ul = orphan.ownerDocument.createElement("ul");

packages/core/src/editor/transformPasted.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@ function retypeLeadingParagraphForEmptyTarget(
204204
return null;
205205
}
206206

207+
// `transformPasted` is also called for drop events, where the slice will be
208+
// inserted at the drop position rather than the current selection. In that
209+
// case the selection-derived target is wrong, so bail out and let the
210+
// default behavior handle drops.
211+
if (view.dragging) {
212+
return null;
213+
}
214+
207215
const blockInfo = getBlockInfoFromSelection(view.state);
208216
const target = blockInfo.isBlockContainer
209217
? blockInfo.blockContent.node

0 commit comments

Comments
 (0)