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

Handle pasted block content #1331

Merged
merged 9 commits into from
Jul 5, 2017
Merged

Handle pasted block content #1331

merged 9 commits into from
Jul 5, 2017

Conversation

ellatrix
Copy link
Member

Follow up from #495 (comment).

Redoing this a bit since the single paragraph approach was merged. This would allow us to do the following:

  • If the pasted content is inline, insert it at caret position. In other words, don't do anything special.
  • If the pasted content has blocks or block level content, split the text block at the caret, parse the content, and insert it between the split content.

@ellatrix
Copy link
Member Author

Some nice visual feedback might be to also select the pasted blocks. Seems like that could prevent disorientation.

@ellatrix
Copy link
Member Author

ellatrix commented Jun 21, 2017

To test:

  • Select two blocks or more, copy, then move the caret to a text block, then paste. The text block should be split at the caret with the pasted blocks inserted in between.
  • Copy some block level content (like this whole comment) and paste it. Behaviour should be the same, but now it's inserted as a freeform block.
  • Inline pasted content should behave as before (I do notice some strange behaviour in master).

@jasmussen
Copy link
Contributor

jasmussen commented Jun 21, 2017

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.

screen shot 2017-06-21 at 14 36 29

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:

screen shot 2017-06-21 at 14 32 39

And if I go to the "New Post" page and click the "Write your story" prompt, I get JS errors:

screen shot 2017-06-21 at 14 38 33

Thank you for working on this 👍

@ellatrix
Copy link
Member Author

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.

@ellatrix
Copy link
Member Author

Errors should be gone.

@ellatrix ellatrix requested review from aduth and removed request for aduth June 21, 2017 12:53
@jasmussen
Copy link
Contributor

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.

👍 👍 just thinking out loud.

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.

👍 👍

Errors should be gone.

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:

Lorem ipsum dolor sit amet, ferri vidisse nam eu, ad nec copiosae mnesarchum vituperatoribus. Te brute dicunt sea, ut vis omnium menandri, ut sumo aliquam has. Eum aperiam interpretaris at, sea et recusabo expetenda, omnis tibique mea no. Pri suas partem ea, ius sonet numquam offendit cu, ad simul admodum pri. Eum cu unum choro albucius.

Eu integre accusata prodesset est, sed te impetus gubergren conceptam, ex sed wisi nostrum ocurreret. Esse velit omittantur ius te, alii dissentias ei vis. At sed unum veritus fabellas. Te volutpat appellantur duo. Ad natum fuisset intellegebat eam, causae invidunt usu id, et vis impetus appetere.

Ea veniam homero eam. Ex inimicus molestiae cum, debet scaevola at eos. Vis assum veritus ut, has ea nostrud accusata, offendit appareat comprehensam ea pro. Ad quo quem veritus appellantur, te est quas phaedrum, eum alia habeo ad. Ei est erroribus imperdiet, omnis dicam propriae sed no. His vitae oratio fierent ne, cu duo tota eligendi, electram rationibus in qui.

Est quis reque cetero ad. Sea id autem nominavi deseruisse. Veniam qualisque definitionem pri id, ea autem feugiat delenit ius, mei at lorem affert accumsan. Dicat eruditi cu est, te pro dicant pericula conclusionemque, ei vim detracto euripidis intellegam. Eius postea volumus mei ad.

Prima ridens denique his te, ferri illum volumus an his. Eu vel dicat homero qualisque, vitae regione deserunt vis ei. Graeci incorrupte liberavisse no mea, saepe voluptaria usu ex, vis dicant euismod id. At dolor reprimique eos, quo altera detraxit moderatius id. Quo iudico utinam eu, ad alia munere mel.

@ellatrix
Copy link
Member Author

@joen I've removed the selection for now. We'll see later.

@ellatrix
Copy link
Member Author

ellatrix commented Jun 21, 2017

@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.

@ellatrix
Copy link
Member Author

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?

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.

@jasmussen
Copy link
Contributor

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.

Up to you :)

@ellatrix
Copy link
Member Author

I'm going to leave that for another PR, because it will bring up a lot of questions.

@jasmussen
Copy link
Contributor

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. 👍 👍

@ellatrix
Copy link
Member Author

Yeah we should change that. :)

@ellatrix ellatrix added the [Type] Enhancement A suggestion for improvement. label Jun 22, 2017
@mtias
Copy link
Member

mtias commented Jun 22, 2017

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).

@mtias
Copy link
Member

mtias commented Jun 22, 2017

By that I mean that we should try to keep this separate from Editable.

@ellatrix
Copy link
Member Author

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?

@mtias
Copy link
Member

mtias commented Jun 22, 2017

I think we would want a /blocks/api/pasting.js to handle all the conversions and block assignments.

@ellatrix
Copy link
Member Author

Cool

@ellatrix
Copy link
Member Author

@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.

@ellatrix
Copy link
Member Author

Fixes #1294 too.

return normaliseToBlockLevelNodes( nodes ).map( ( node ) => {
// To do: move to block registration.
if ( node.nodeName === 'P' ) {
return createBlock( 'core/text', {
Copy link
Contributor

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)

Copy link
Member Author

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.

Copy link
Member Author

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.

@ellatrix
Copy link
Member Author

Let me see if I can get rid of the node logic in the registration as well.

@ellatrix
Copy link
Member Author

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.

@@ -29,13 +29,15 @@ registerBlockType( 'core/text', {
content: query( 'p', children() ),
},

pasteMatcher: ( node ) => node.nodeName === 'P',
Copy link
Contributor

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.

} );
const name = type ? type.name : getUnknownTypeHandler();
const attributes = type
? getBlockAttributes( type, node.outerHTML )
Copy link
Contributor

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?

Copy link
Member Author

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 );
}
Copy link
Contributor

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?

Copy link
Member Author

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.

@ellatrix
Copy link
Member Author

ellatrix commented Jul 1, 2017

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' ) )
Copy link
Contributor

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.

Copy link
Member Author

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?

Copy link
Contributor

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' &&
Copy link
Contributor

@youknowriad youknowriad Jul 3, 2017

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?

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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

@youknowriad
Copy link
Contributor

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 -1 maybe?

@ellatrix
Copy link
Member Author

ellatrix commented Jul 3, 2017

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 -1 maybe?

For that we'll have to start passing focus props as well. Maybe.a good iteration?

@ellatrix
Copy link
Member Author

ellatrix commented Jul 3, 2017

Any blockers? I'd really love to move on with this. If not, I'll merge soon.

@mtias
Copy link
Member

mtias commented Jul 3, 2017

Haven't had a chance to properly read through this one, but it should not block merging as a first step.

@ellatrix ellatrix merged commit b063be3 into master Jul 5, 2017
@ellatrix ellatrix deleted the try/paste-blocks branch July 5, 2017 12:22
matcher: ( node ) => (
node.nodeName === 'P' &&
// Do not allow embedded content.
! node.querySelector( 'audio, canvas, embed, iframe, img, math, object, svg, video' )
Copy link
Member

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@aduth aduth Aug 31, 2017

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?

youknowriad pushed a commit that referenced this pull request Jan 17, 2020
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants