GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.38k stars 4.06k forks source link

Weird issue with internal drag&drop - Dropped contents === "null" #1797

Closed arachnosoft closed 4 years ago

arachnosoft commented 5 years ago

Hi @artf ,

I'm facing a very strange issue related to HTML5 drag & drop.

When I select some mixed contents within grapesjs, including editable text contents and the surrounding components as a whole selection with the mouse, the dropped (copied) contents is sometimes "null" (a string carrying the "null" text value, not a JavaScript null).

I've managed to reproduce the issue with the Newsletter demo, with Chrome 71.0.3578.98. I don't know whether it happens with other browsers, or not...

grapesjs null dragend

When the issue arises, I've noticed that, when catching the canvas:dragdata event, the result.content variable is "null" (string). I looked into the call stack and found that this value comes from the dataTransfer.getData("text/html") call in grapesjs' core Droppable.getContentByData() function:

} else if ((0, _underscore.indexOf)(types, 'text/html') >= 0) { content = dataTransfer.getData('text/html').replace(/<\/?meta[^>]*>/g, '');

I don't know why, and in which circumstances the browser sends "null" while fetching data in text/html format. All other formats seem to carry a correct value. You'll note that I use the CKEditor plugin, hence I have some additional "formats" related to CKEditor in dataTransfer:

chrome_2019-02-14_15-57-49

Could you check whether you can reproduce this on your own side, and if you can do anything to handle this case within grapesjs? (assuming that it may be a browser-related bug).

In the meantime, I handled this on my own side by picking the value from dataTransfer.getData("text/plain") when possible, and returning empty stuff otherwise:

myGrapesJsObject.on('canvas:dragdata', function (dataTransfer, result) {
            if (result && result.content) {
                if (typeof (result.content) == "string" && result.content === "null" && dataTransfer.getData("text/html") === result.content) {
                    result.content = "";
                    if (dataTransfer.types && dataTransfer.types.length > 0) {
                        for (var i = 0; i < dataTransfer.types.length; i++) {
                            if (dataTransfer.types[i] == "text/plain") {
                                var altValue = dataTransfer.getData(dataTransfer.types[i]);
                                if (altValue && altValue != "") {
                                    result.content = altValue;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }

As a side note, I sometimes found that the dataTransfer object carries some "old" dropped contents from previous drags, and not the latest stuff. But I've been unable to find when and why so far, so I'll not bother you with this for now (unless it's a known issue, and if you need additional information on this particular issue.)

Thanks for your thoughts!

artf commented 5 years ago

Thanks for the report @arachnosoft BTW I'm able to reproduce it, but only once the first CKEditor instance is activated, can you confirm?

arachnosoft commented 5 years ago

Yeah @artf , seems to be the exact test case...

If I refresh the page to get a clean context, empty the canvas, drop a Grid component (quite complex one), select some of its text and surrounding cells WITHOUT activating CKEditor, and drag/drop it from the text, it's working correctly.

But as soon as I double-click on a cell to activate CKEditor, click elsewhere to disable editing, and follow the exact same steps as above (making the exact same selection and drag/drop), the dropped contents is "null".

grapesjs issue 1797 1

And, unless I refresh the page to get rid of all CKEditor instances in the DOM, the issue remains, even if I empty the canvas first:

grapesjs issue 1797 2

Glad you reproduced it as well, because it was quite a pain to find what was going wrong!

I noticed that a new CKEditor instance was being created into the DOM each time a new component is edited, even though the CKEditor plugin uses CKEditor's inline() method. I don't know if this is "by-design", or if it could explain that kind of issue.

artf commented 5 years ago

I noticed that a new CKEditor instance was being created into the DOM each time a new component is edited, even though the CKEditor plugin uses CKEditor's inline() method. I don't know if this is "by-design", or if it could explain that kind of issue.

Yeah this is how their inline plugins works but I don't think this somehow related

BTW definitely CKEditor adds its type to dataTransfer object and edits, for some reason (probably to handle some their edge cases), the 'text/plain' (GrapesJS relies completely on native objects without touching them).

v8jupiter commented 4 years ago

Hello, someone have any solution for this?

artf commented 4 years ago

Nope, and, unfortunately, I have to close it as it seems to be an issue from the CKEditor behavior