-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Handle pasted block content #1331
Conversation
Some nice visual feedback might be to also select the pasted blocks. Seems like that could prevent disorientation. |
To test:
|
I just pasted some random lorem ipsum into a text block, and voila, got the freeform block. Nice! I'm assuming we can't detect that it's just multiple paragraphs and create multiple paragraph blocks, correct? I understand the idea of wanting to highlight the newly pasted block on paste, though invoking the multi select seems also a bit confusing. Could we simply have the new block focused? I.e. I'm waffling a bit on whether we should actually split a paragraph if you set the caret in between it and paste multiple paragraphs. Perhaps in that case we should just insert it after the block the caret is in. What do you think? I'm getting this error in the console btw: And if I go to the "New Post" page and click the "Write your story" prompt, I get JS errors: Thank you for working on this 👍 |
Thanks for the feedback @joen. We can look at parsing those as single text blocks for paste, but thought it's better to get this in in smaller steps. Yeah inserting after the block was the approach before this PR, but I'm honestly not a big fan. It's confusing to me. Paste means insert at caret. I'll look at the errors now. |
Errors should be gone. |
👍 👍 just thinking out loud.
👍 👍
Errors are gone 🎉 Copying multiple blocks and pasting works great now! I still think it's a little jarring to have them all be selected after the fact, but I'd be willing to try this out in master for a bit, if you feel strongly for it. Something that worked in the previous version of this branch seems to have broken now, I think. I used to be able to paste the following into a text block and I'd get a freeform block. Now it just pastes it in the text block, linebreaks and everything:
|
e4ca754
to
c0d880d
Compare
@joen I've removed the selection for now. We'll see later. |
@joen Hm, I'm not sure how that will have worked before. I have a feeling I should handle the paragraphs in this PR as well and get it over with, instead of trying to do it in smaller pieces. |
Thinking more about this, I don't mind to come back to this later and decide something else, but maybe it's good to try for now. It's fairly trivial to adjust. Another reason I like the splitting more is that it gives you more options. You can still move the caret to the end and insert there. |
Up to you :) |
I'm going to leave that for another PR, because it will bring up a lot of questions. |
Nice! Last change works really well. If I paste into an existing, empty text block, that empty text block stays empty and the pasted content shows up after. This is fine, even though it would be nice if the empty text block gets the first of multiple paragraphs pasted. Up to you whether that's intended (can be seen as destructive) or not. 👍 👍 |
49cd756
to
e2bab10
Compare
Yeah we should change that. :) |
To handle pasting in general and convert to blocks we are going to need a special layer where we can isolate the responsibility and handle different sources (google docs, word documents, etc). |
By that I mean that we should try to keep this separate from |
Yeah, I agree. It wasn't my original intention to also start conversion paragraphs, only have the inline-block distinction. I can move this to a separate file now. Where should it belong? |
I think we would want a |
Cool |
af571fd
to
c6eb475
Compare
@matias Moved to a separate file. I haven't added it to the block registration yet as this might need some discussion. Blocks could register a method to decide if the want to convert a given node or not, but maybe it could be automated as well if blocks had a schema. In any case, maybe this material for another PR. |
Fixes #1294 too. |
blocks/api/paste.js
Outdated
return normaliseToBlockLevelNodes( nodes ).map( ( node ) => { | ||
// To do: move to block registration. | ||
if ( node.nodeName === 'P' ) { | ||
return createBlock( 'core/text', { |
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 kind of bothers me a bit. I mean we're hardcoding text
and freeform
block here while we're not even sure these blocks are registered and how they work. Ideally, there should be some block API to handle this. Something like this #413 (Sorry if I missed some discussions on this)
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.
That's what I meant with needing API work.
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'll add it to the registration like this for now.
Let me see if I can get rid of the node logic in the registration as well. |
I'd leave it at this for now. If anyone has ideas, I can look into adding those. Ideally, at a later point, we match against a block schema to decide whether a block can handle the content or not. Maybe a block con have some additional content upgrading schemas. Cc @nylen. |
blocks/library/text/index.js
Outdated
@@ -29,13 +29,15 @@ registerBlockType( 'core/text', { | |||
content: query( 'p', children() ), | |||
}, | |||
|
|||
pasteMatcher: ( node ) => node.nodeName === 'P', |
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.
Could we find a more "generic" name, because this could be used to convert legacy posts too.
blocks/api/paste.js
Outdated
} ); | ||
const name = type ? type.name : getUnknownTypeHandler(); | ||
const attributes = type | ||
? getBlockAttributes( type, node.outerHTML ) |
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 certain it will always be possible to use getBlockAttributes
, we could decide to match a different HTML than the "serialized" one (for example a shortcode for the image with caption). But we could live this for later.
I think the matcher
api could be merged with the transforms
api:
transforms: {
from: [
type: 'raw',
matcher: ( node ) => true,
transform: ( rawHtml ) => block,
]
}
Thoughts?
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.
Sure, that's not a bad idea. :) I guess we can also separately match to pasted block to the schema as well, and use the transform API for upgrading content.
// blocks. | ||
} else if ( childNodes.some( isBlockPart ) ) { | ||
blocks = pasteHandler( childNodes ); | ||
} |
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.
Maybe we could split out the logic here out of the Editable
into the parse.js
file. I mean the pasteHandler
could probably check for the block delimiters itself, don't you think?
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.
Actually, I think part of this should be in Editable because it needs to decide what content it con handle itself (inline), and then pass the rest to the paste handler. This can probably be improved though. Maybe we should only do the black check and leave out the comment check.
2bbc015
to
9f3ecef
Compare
Added some more matchers. E.g. go to #1352 and copy the issue content, then paste it in Gutenberg. Images are converted too. |
type: 'raw', | ||
matcher: ( node ) => ( | ||
node.nodeName === 'IMG' || | ||
( ! node.textContent && node.querySelector( 'img' ) ) |
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.
Shouldn't we be stricter here? I'm afraid we'd use lose here. I=agin a div
containing an img
and several paragraphs. We'd lose everything but the img.
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.
Doesn't node.textContent
check for that?
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 misread the condition.
{ | ||
type: 'raw', | ||
matcher: ( node ) => ( | ||
node.nodeName === 'P' && |
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.
What if the p
has custom attributes (styles, classnames ...), should we match it or not?
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.
Note that this is paste, not legacy content. Fine with matching current alignment classes.
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.
Not sure why we should treat pasting and legacy content differently. IMO we should avoid losing data (means we should match if the classnames are known but not if it uses custom classnames or other attributes).
That said, I understand than this PR puts in place the base architecture for the pasting and legacy content feature and I'm fine tweaking those over time.
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 should treat it differently. In a paste context, you're dropping IDs, classes, style attributes, empty elements, etc. while for legacy content you are not.
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.
Good point, maybe we should consider adding a boolean parameter to the matcher function. These transformations will be really similar
I've tried this and the focus is moved to the first pasted block, Should we move the focus to the last pasted block instead with an offset |
For that we'll have to start passing focus props as well. Maybe.a good iteration? |
Any blockers? I'd really love to move on with this. If not, I'll merge soon. |
Haven't had a chance to properly read through this one, but it should not block merging as a first step. |
7c066a2
to
c127df6
Compare
matcher: ( node ) => ( | ||
node.nodeName === 'P' && | ||
// Do not allow embedded content. | ||
! node.querySelector( 'audio, canvas, embed, iframe, img, math, object, svg, video' ) |
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.
How did we determine which node types to include here?
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.
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.
A follow-up question: Why did we exclude embedded content? Was this meant to allow other raw matchers for embedded content to take precedence?
* wip - ios multiple * update gutenberg ref * update bundle ios * update ref * update ref * update example app * fix methods parameters description and use constants for dictionary keys * add android support * update js bundle * add RNMedia interface and use it * add multiple upload functionality for android * support multiple selection of video from device * update ref * Update bundles * update bundle and fix ids in tuple * update ref * update ref * update bundle * update js
Follow up from #495 (comment).