facebookarchive / draft-js

A React framework for building text editors.
https://draftjs.org/
MIT License
22.56k stars 2.64k forks source link

Copy/paste between editors #787

Open bryanph opened 7 years ago

bryanph commented 7 years ago

How can I make sure custom blocks and entities are preserved if I copy from one Draft editor to another? Hence, I want the custom block/entity to be recreated in the new editor.

Take for example the entity example in the repo, when copying from one draft instance to another, the entities are not copied along, just the raw text. The same happens with custom blocks, for example in the media example

bryanph commented 7 years ago

I think one way to implement this is to set the required data for the blocks and entities in the ClipboardData object when the user triggers a copy event. Then on a paste event you check if the required data is set in the ClipboardData and if so, use that data to paste the entities and custom blocks into the second editor (by cloning the entities and inserting the blocks).

What do you think?

StorytellerCZ commented 7 years ago

Yes, but won't that interfere with copying into non-draft editors?

bryanph commented 7 years ago

@StorytellerCZ It seems like you can just set arbitrary data in the DataTransfer object keyed by a "type", see https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData . No idea if this is an accepted practice though.

In the example below I do that. One thing to be cautious of though is that every time a user copies editor data, you must serialize the content state which is quite costly. No

<html>
    <script>
    function addExtraData(e) {
        var clipboardData = e.clipboardData;

        var someObj = {
            "test": { "test": "test" }
        }

        var selection = window.getSelection()

        console.log("called addExtraData")
        console.log(selection)

        e.clipboardData.setData('text/plain', selection)
        e.clipboardData.setData('test', JSON.stringify(someObj))

        e.preventDefault()
    }

    function getExtraData(e) {
        var data = e.clipboardData.getData('text/plain')
        var data2 = e.clipboardData.getData('test')
        console.log("called getExtraData")
        console.log(data)
        console.log(JSON.parse(data2))
    }

    document.addEventListener('copy', addExtraData);
    document.addEventListener('paste', getExtraData);

    </script>
    <body>
        <p>
            Text to Copy
        </p>
        <textarea rows="4" cols="50"></textarea>
    </body>
</html>
mitermayer commented 7 years ago

We are currently working on a solution for this issue

christophepas commented 7 years ago

Hey @mitermayer ! Glad to know this is wip, have you any update on a solution for this ?

Apart from mentions this is also a problem for nested lists, and in general an onCopy method added to the Editor API could help solve these issues.

trevtrich commented 7 years ago

@mitermayer has there been any movement on this? We could really use this functionality. Right now we're considering forking and working on a solution. Would you guys be open to a PR if we come up with something we're happy with?

Assuming dev on this has stopped and we start implementing a solution, would appreciate any guidance on major problems you ran into that we might hit as well.

flarnie commented 6 years ago

Just an update - I don't expect any support for this in the near future. It would be an interesting long term goal but we have shifted to have internal folks work on some other fixes and features for Draft.

mzedeler commented 6 years ago

I am going to need this and expect to be working on a solution within a month or two.

thibaudcolas commented 6 years ago

I have been able to make this work a bit better by leveraging the internal Draft.js editor clipboard, at least if the copy-paste happens between editors that are on the same page (quite a common circumstance in a CMS that uses Draft.js).

The basic idea is:

  1. When editors mount (componentDidMount), register a ref to their DraftEditor component in a global object (eg. window.myEditorRefs). Associate the ref with the editor's getEditorKey (internal method related to the editorKey prop) – make sure these are unique between all editors if you set it manually.
  2. When editors unmount, un-register the ref.
  3. In handlePastedText, check whether the paste contains an editor key (data-editor) like Draft.js does internally: https://github.com/facebook/draft-js/blob/4c4465f6c05b6dbb9eb769f98e659f917bbdc0f6/src/component/handlers/edit/editOnPaste.js#L122-L130
  4. If it does, find the corresponding editor in your registry of editor refs.
  5. Get the clipboard data from its internal clipboard, and insert it as a fragment.

I only put this together in half an hour so might be overlooking something (and the code is horrible): https://github.com/springload/draftail/pull/148


Of course this would be much easier if there was a way to directly set the editor's clipboard on the copy data (https://github.com/facebook/draft-js/blob/4c4465f6c05b6dbb9eb769f98e659f917bbdc0f6/src/component/handlers/edit/editOnCopy.js#L25-L36), and then retrieve it in the paste (like @bryanph shows above, except with Draft.js data). That said I'm not that familiar with clipboard data handling in browsers so don't know exactly if that would actually work.


I'll try this further over the next few days and report back with more findings.

mzedeler commented 6 years ago

I've done some investigation and can see that I am forced to write my own replacement of the copy/paste-behavior, because I have extra data that isn't stored in any of the internal Draft data structures.

My plan is to ensure that Draft never tries to use the internal clipboard and then populate the normal clipboard with HTML fragments that have been enriched with the data that I keep outside the normal Draft data structures.

I think it could be a worthy long term goal to try to replace Drafts internal clipboard with a behavior where the data on the normal clipboard is sufficient for Draft when pasting. By doing this, we can remove a source of unpredictable behavior in Draft.

thibaudcolas commented 6 years ago

I've worked on this further since https://github.com/facebook/draft-js/issues/787#issuecomment-382547599 and managed to fix the issue in my editor. Also submitted a similar fix for Draft.js itself over at #1784.

If you don't want to wait for that PR to be merged, the details on how to fix this in your own project are at the end of #1784.

dav-sap commented 3 years ago

Is thing something that might be fixed soon? Its a common use case

mzedeler commented 3 years ago

I think it is a relevant comment, @dav-sap, but the development of Draft.js is progressing extremely slowly. As you can see, the proposed fix by Thibaud has been open for more than two years.

yornaath commented 2 years ago

Currently building a rich text editor with alot of entity usage and copy pasting between documents is a huge use case. Any progress on getting this to prod?

bryanph commented 2 years ago

@gorillatron Honestly, I moved away from draft-js a long time ago and I feel there's no reason to start using this in a new project, there are much better alternatives out there. This project has been in maintenance mode for a long time now. Look into slate or prosemirror for better approaches to the rich text editing problem.