-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Transfer selected content via HTML clipboard on copy-paste between editors #1784
Transfer selected content via HTML clipboard on copy-paste between editors #1784
Conversation
editor.setClipboard(fragment); | ||
|
||
// IE11 does not support ClipboardEvent.clipboardData. | ||
if (e.clipboardData && fragment) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think skipping IE11 here is fair, since it doesn't handle HTML in the clipboard to start with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We handle IE11 in our fork in a rather different way. We have two hidden divs, one for copy and one for paste, that exist to accept or receive incoming content. In the copy case we copy the HTML of the component into this div and temporarily move selection so that the browser is able to copy that content in the way it wants to. For paste we temporarily move focus to the other div and then grab the content from there and convert it into a fragment.
The trick is ensuring those hidden divs are styled the same as the editor and that the focus games play out correctly but it works extremely well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@colinjeanne could you share your code's fork, at least the part that's relevant to this? I think I'd be keen to add this to my editor as well, depending on how much code it takes.
Edit: I see textioHQ@86b8a41 from #986.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there are a few different changes, the first was textioHQ#21 followed later by textioHQ#26. We originally wrote this solution in order to get HTML into the clipboard even for browsers that didn't support adding it explicitly.
|
||
const fragmentElt = document.createElement('div'); | ||
const domSelection = window.getSelection(); | ||
fragmentElt.appendChild(domSelection.getRangeAt(0).cloneContents()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is taken from Rangy: /~https://github.com/timdown/rangy/blob/1e55169d2e4d1d9458c2a87119addf47a8265276/src/core/domrange.js#L515-L520.
Also from what I understand modern browsers only support one range in the selection.
|
||
try { | ||
// If JSON parsing fails, handle the paste as normal HTML. | ||
rawContent = JSON.parse(fragmentAttr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure in what scenario that would occur – perhaps if the clipboard gets truncated somehow.
Any update on this? I think I'm having an issue that could be solved with this PR |
@pablopunk this is working really well in my editor via the separate package I made available (/~https://github.com/thibaudcolas/draftjs-conductor#idempotent-copy-paste-between-draftjs-editors). If you have time to try it out, I'm sure more feedback would help the PR. |
Thanks for this @thibaudcolas, and packaging it so nicely! We are having the same issues. Facebook, any plans to merge this? |
Thanks @thibaudcolas for submitting this & other folks for confirming that this is working for you :) For this change, I want to make sure that it doesn't break any existing copy-paste behavior on facebook.com or our internal editors. Will import into our stack and test it soon - thanks for your patience! |
That's great to hear @niveditc! I haven't received any bug reports from the users of my editor since putting this in production in august 2018. That said, there are some sources of bugs I can easily think of:
For the last issue, this is the most problematic IMO but is actually already happening when pasting rich text with links or images in an editor that doesn't support them. So in a sense it’s nothing new, it will just become more likely if the copy/paste retains more content. I haven't had this issue with my editor because I also made a filtering library (/~https://github.com/thibaudcolas/draftjs-filters) so copy/pasting only keeps what the editor actually supports. |
…ustom entities. Summary: details are here: #1784 in the comment, nivedita mentioned we need to test this out for intern editor hence not merging the PR. since that comment was made back in Jan, so it's probably gonna take a while for this to be merged into FB codebase. In the mean time this is blocking CMS Editor's embeded CMS use case. So I figured we should do a branching to merge the fix just for CMS Editor. Unfortunately, the logic is pretty deep in the DraftJS stack, so I had to make a couple changes to pass down the copy paste behavior. Differential Revision: D14626028 fbshipit-source-id: 4826ea33abf2618835841200af81e10d707aa23b
…ustom entities. Summary: details are here: facebookarchive#1784 in the comment, nivedita mentioned we need to test this out for intern editor hence not merging the PR. since that comment was made back in Jan, so it's probably gonna take a while for this to be merged into FB codebase. In the mean time this is blocking CMS Editor's embeded CMS use case. So I figured we should do a branching to merge the fix just for CMS Editor. Unfortunately, the logic is pretty deep in the DraftJS stack, so I had to make a couple changes to pass down the copy paste behavior. Differential Revision: D14626028 fbshipit-source-id: 4826ea33abf2618835841200af81e10d707aa23b
…ustom entities. Summary: details are here: facebookarchive#1784 in the comment, nivedita mentioned we need to test this out for intern editor hence not merging the PR. since that comment was made back in Jan, so it's probably gonna take a while for this to be merged into FB codebase. In the mean time this is blocking CMS Editor's embeded CMS use case. So I figured we should do a branching to merge the fix just for CMS Editor. Unfortunately, the logic is pretty deep in the DraftJS stack, so I had to make a couple changes to pass down the copy paste behavior. Differential Revision: D14626028 fbshipit-source-id: 4826ea33abf2618835841200af81e10d707aa23b
…ustom entities. Summary: details are here: facebookarchive#1784 in the comment, nivedita mentioned we need to test this out for intern editor hence not merging the PR. since that comment was made back in Jan, so it's probably gonna take a while for this to be merged into FB codebase. In the mean time this is blocking CMS Editor's embeded CMS use case. So I figured we should do a branching to merge the fix just for CMS Editor. Unfortunately, the logic is pretty deep in the DraftJS stack, so I had to make a couple changes to pass down the copy paste behavior. Differential Revision: D14626028 fbshipit-source-id: 4826ea33abf2618835841200af81e10d707aa23b
wow @thibaudcolas thanks a lot for the detailed explanation I'll try using your package for this! |
My pleasure @yasuf 👌 Let me know if you run into any hiccups. |
I’ve updated the user-land implementation in draftjs-conductor to use the new The next step would be to switch to using a custom MIME type to transfer the pasted content ( |
Closing because of #3136. I’m glad I didn’t waste any further time on this PR! |
Summary
This PR changes how Draft.js handles copy-cut-paste between editors so all content is kept as-is. I’ve been working on this as an external package at /~https://github.com/thibaudcolas/draftjs-conductor, but there is no reason for this not to just be fixed in Draft.js directly.
On copy, we store the selected content within the clipboard’s HTML, overriding the browser’s default copy/cut handling, so that the Draft.js paste processor can then retrieve content from the serialised "Raw" content instead of relying on HTML parsing.
This fixes the following issues: #380, #787, #1389, #1163, #1154, #1605 (review), and #523 but only for the copy-paste case, #523 (comment). Here's what it looks like in practice, copy-pasting between two tabs in the media example:
As-is, the new copy/cut behavior and HTML clipboard generation doesn't replicate browsers’ behavior of inlining styles into the clipboard content (e.g. if you copy content in a particular font into Google Docs, browsers will try to preserve that). This seems like an advantage to me, but might be worth assessing further. For example, it means that copy-pasting nested lists into Google Docs will no longer add extra indentation to the (otherwise not nested anymore) list items there.
Edit: one last thing – I put the serialised content inside
text/html
instead of using a separate mime type for two reasons. First one is that without this PR, Draft.js only offered access to HTML viahandlePastedText
for me to hook into. Second reason is that I've heard some browsers didn't support custom mime types. Fixing this issue in Draft.js itself, we could revisit this.Test Plan
editOnCopy
– I don't think I've seen any yet, would welcome some direction on how to test this.DraftPasteProcessor
Before making this PR, I first fixed this issue for my own editor. If you want to learn more, have a look at wagtail/wagtail#4582.
If you want to use this in your own editor without waiting for the PR to be merged, I made my fix available as a separate package: /~https://github.com/thibaudcolas/draftjs-conductor#idempotent-copy-paste-between-draftjs-editors. Here's what it looks like to use it:
Then: