Editor's paste behavior with headings

Hi!

Why the origin heading become a paragraph after paste? Steps to reproduce:

  1. Go to https://prosemirror.net/
  2. Initial HTML structure:
<h1>Heading</h1>
<p></p>
  1. Select content including the heading:
<h1>[from]Heading</h1>
<p>[to]</p>
  1. Copy the selection
  2. Set cursor position before “Heading”:
<h1>[from][to]Heading</h1>
<p></p>
  1. Paste

Current result

<h1>Heading</h1>
<p>[from][to]Heading</p>
<p></p>

Expected result

<h1>Heading</h1>
<h1>[from][to]Heading</h1>
<p></p>

Is there a way to preserve both headings in this case? I tried using transformPasted, but I suppose this behavior is related to the Transform.replace logic.

Fixing this case would make edit experience more solid.

Any insights or suggestions on it would be greatly appreciated. Thank you!

1 Like

You need to merge copy nodes yourself.

Thank you! Your suggestion pushed me to some kind of solution. Here is an example https://codesandbox.io/p/sandbox/weathered-sun-94v6vw

The idea is simple: if the clipboard contains a paragraph at the end, style it like the target node at the selection’s end.

It would be cool to have this behavior by default.

 transformPasted(slice, view) {
      let transformedSlice: Slice | null = null;

      const { selection } = view.state;
      const { $to } = selection;

      const { lastChild } = slice.content;

      const isParagraphAtEnd =
        lastChild && lastChild?.type.name === "paragraph";

      if (
        slice.content.childCount > 1 &&
        isParagraphAtEnd &&
        $to.parent.type.compatibleContent(lastChild.type)
      ) {
        const nodes: PMNode[] = [];

        slice.content.forEach((node, _offset, index) => {
          const isLast = index === slice.content.childCount - 1;

          if (isLast) {
            const transformedNode = $to.parent.type.create(null, node.content);
            nodes.push(transformedNode);

            return;
          }

          nodes.push(node);
        });

        transformedSlice = new Slice(
          Fragment.fromArray(nodes),
          slice.openStart,
          slice.openEnd
        );
      }

      return transformedSlice || slice;
    },
2 Likes