ProseMirror / prosemirror

The ProseMirror WYSIWYM editor
http://prosemirror.net/
MIT License
7.66k stars 336 forks source link

control / fix behaviour of normalizeSiblings on paste #1430

Closed YousefED closed 10 months ago

YousefED commented 10 months ago

I have a schema with an extra level of nesting, see demo setup @ https://codesandbox.io/s/gifted-shape-xmvs84?file=/src/components/App/App.js

I'm running into trouble making this work with paste handling. When pasting 2 paragraphs, the second paragraph gets nested in the first:

image

When pasting 3 paragraphs, all 3 are turned into top-level elements:

image

Of course, both constructs are valid in the schema, but I find it weird that there's a difference in behavior when pasting 2 vs 3 paragraphs. The behaviour I'm looking for is to get top-level paragraphs only (so, when pasting 3 paragraphs it works well, but when pasting 2 paragraphs, the nesting is undesirable).

I tried debugging, and think the part where this happens is normalizeSiblings, but it's difficult to figure out what exactly needs to be changed to support this.

marijnh commented 10 months ago

It tries to fit the content at the cursor position, which is possible in the first case (paste the rest of the paragraph at the start into the focused textblock, add the container after that as a sibling) but not in the second (because you can't add multiple containers after a paragraph). You may be able to override this with a transformPasted hook that closes the slice at the start when it looks like that is a sequence of container nodes.

YousefED commented 10 months ago

Thanks for the pointers @marijnh. While I couldn't get to "closing the slice" succesfully, I took a different approach.

I noticed the slice in transformPasted is [paragraph<...>, container<paragraph<...>].

By using the following code to wrap the first paragraph in a container, it seems to work:

transformPasted(slice, view) {
          let f = Fragment.from(slice.content);
          for (let i = 0; i < f.childCount; i++) {
            if (f.child(i).type.spec.group !== "container") {
              const container = view.state.schema.nodes.container.create(
                undefined,
                f.child(i)
              );
              f = f.replaceChild(i, container);
            }
          }
          return Slice.maxOpen(f);
        },

Do you think this approach makes sense? Or am I missing a more obvious solution?

marijnh commented 10 months ago

That sounds like it should work.