Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy/paste between editors #787

Open
bryanph opened this Issue Nov 16, 2016 · 11 comments

Comments

Projects
None yet
8 participants
@bryanph
Copy link

commented Nov 16, 2016

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

This comment has been minimized.

Copy link
Author

commented Nov 16, 2016

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

This comment has been minimized.

Copy link

commented Nov 27, 2016

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

@bryanph

This comment has been minimized.

Copy link
Author

commented Nov 27, 2016

@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 mitermayer self-assigned this Dec 16, 2016

@mitermayer

This comment has been minimized.

Copy link
Member

commented Dec 16, 2016

We are currently working on a solution for this issue

@christophepas

This comment has been minimized.

Copy link

commented Jan 13, 2017

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.

@richardson-trevor

This comment has been minimized.

Copy link
Contributor

commented Aug 30, 2017

@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

This comment has been minimized.

Copy link
Contributor

commented Sep 29, 2017

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

This comment has been minimized.

Copy link

commented Mar 5, 2018

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

@thibaudcolas

This comment has been minimized.

Copy link
Contributor

commented Apr 18, 2018

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:
    // If the editorKey is present in the pasted HTML, it should be safe to
    // assume this is an internal paste.
    html.indexOf(editor.getEditorKey()) !== -1 ||
    // The copy may have been made within a single block, in which case the
    // editor key won't be part of the paste. In this case, just check
    // whether the pasted text matches the internal clipboard.
    (textBlocks.length === 1 &&
    internalClipboard.size === 1 &&
    internalClipboard.first().getText() === text)
  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): springload/draftail#148


Of course this would be much easier if there was a way to directly set the editor's clipboard on the copy data (

function editOnCopy(editor: DraftEditor, e: SyntheticClipboardEvent<>): void {
var editorState = editor._latestEditorState;
var selection = editorState.getSelection();
// No selection, so there's nothing to copy.
if (selection.isCollapsed()) {
e.preventDefault();
return;
}
editor.setClipboard(getFragmentFromSelection(editor._latestEditorState));
}
), 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

This comment has been minimized.

Copy link

commented Apr 19, 2018

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

This comment has been minimized.

Copy link
Contributor

commented Jun 10, 2018

I've worked on this further since #787 (comment) 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.