Hi!
Why the origin heading become a paragraph after paste? Steps to reproduce:
Go to https://prosemirror.net/
Initial HTML structure:
<h1>Heading</h1>
<p></p>
Select content including the heading:
<h1>[from]Heading</h1>
<p>[to]</p>
Copy the selection
Set cursor position before “Heading”:
<h1>[from][to]Heading</h1>
<p></p>
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.
import {
editorViewOptionsCtx,
parserCtx,
schemaCtx,
serializerCtx,
} from '@milkdown/core'
import { getNodeFromSchema } from '@milkdown/prose'
import type { Node, Slice } from '@milkdown/prose/model'
import { DOMParser, DOMSerializer } from '@milkdown/prose/model'
import { Plugin, PluginKey, TextSelection } from '@milkdown/prose/state'
import { $prose } from '@milkdown/utils'
type UnknownRecord = Record<string, unknown>
function isPureText(
content: UnknownRecord | UnknownRecord[] | undefined | null
): boolean {
if (!content) return false
if (Array.isArray(content)) {
if (content.length > 1) return false
return isPureText(content[0])
This file has been truncated. show original
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