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

Already on GitHub? Sign in to your account

Image Block: Adding image resizing handles #2213

Merged
merged 11 commits into from Aug 9, 2017

Conversation

Projects
None yet
4 participants
Collaborator

youknowriad commented Aug 4, 2017

closes #600

Implementation notes

  • We need to know the original image width and height before allowing to resize the image, this is done by loading the image before showing it and limiting its width/height to the canvas size (see ImageSize component)

Testing instructions

  • Try inserting and resizing an image

Questions

Should we resize the container (image + caption) or just the image?

@youknowriad youknowriad self-assigned this Aug 4, 2017

@youknowriad youknowriad requested a review from jasmussen Aug 4, 2017

Collaborator

jasmussen commented Aug 4, 2017

Holy guacamole you're so fast it's hard to keep up!

This works really really well.

I can't decide whether we should change the block behavior when the image has no alignment. But it does bring some challenges with it, when it comes to floats and centering:

screen shot 2017-08-04 at 11 35 11

screen shot 2017-08-04 at 11 35 19

It seems right now we hard-code a width when we float. Can we undo that?

I have a bad feeling we might need to revisit the full-wide hack we've been using, or at least rethink how images float in such a world. Which is on me, but let's see what we can do with the floated block width for now...

Thanks Riad!

Collaborator

jasmussen commented Aug 4, 2017

There appear to be some issues with the demo content in this branch:

screen shot 2017-08-04 at 11 42 09

Collaborator

youknowriad commented Aug 4, 2017

@jasmussen Fixed the demo content bug but for the left/right width it's way harder. The width is fixed using the $float-margin mixin but It's hard to override when the width is dynamic. I think you're right, we need to revisit the full-wide hack we've been using to make this work.

All the help here would be appreciated (I don't have all the context about this hack) and maybe we could still ship this and resolve the left/right fixed width in another PR by changing the hack.

.eslintrc.json
@@ -24,7 +24,8 @@
"wp": true,
"wpApiSettings": true,
"window": true,
- "document": true
+ "document": true,
+ "Image": true
@aduth

aduth Aug 4, 2017

Member

See also: #1008

It was intentional to remove all globals and encourage to access through window, i.e. window.Image. Could be easy to think one had assigned a local Image variable but in fact it's referencing the global (as in the case of event bug of #1007). I guess same could be said of document (window.document), but... ¯\_(ツ)_/¯

blocks/library/image/image-size.js
+ this.image.onload = () => {
+ const maxWidth = this.container.clientWidth;
+ const ratio = this.image.height / this.image.width;
+ const width = this.image.width < maxWidth ? this.image.width : maxWidth;
@aduth

aduth Aug 4, 2017

Member

Math.min could come in handy here:

const width = Math.min( maxWidth, this.image.width );
blocks/library/image/image-size.js
+ const maxWidth = this.container.clientWidth;
+ const ratio = this.image.height / this.image.width;
+ const width = this.image.width < maxWidth ? this.image.width : maxWidth;
+ const height = this.image.width < maxWidth ? this.image.height : maxWidth * ratio;
@aduth

aduth Aug 4, 2017

Member

Same note about Math.min, but also: Is maxWidth the correct value to test against? Or should maxHeight be a separate variable maxHeight = maxWidth * ratio ?

@youknowriad

youknowriad Aug 4, 2017

Collaborator

maxWidth is the correct value, we adapt the height only if the image don't fit in the container's width.

blocks/library/image/image-size.js
+ const ratio = this.image.height / this.image.width;
+ const width = this.image.width < maxWidth ? this.image.width : maxWidth;
+ const height = this.image.width < maxWidth ? this.image.height : maxWidth * ratio;
+ this.setState( { width, height } );
@aduth

aduth Aug 4, 2017

Member

What if the component has unmounted by this point?

@youknowriad

youknowriad Aug 4, 2017 edited

Collaborator

There's a componentWillUnmount call.

Edit: I have a typo there

@aduth

aduth Aug 4, 2017

Member

Oh, missed that. Is changing the src while the request is in flight enough to prevent onload from being called?

@aduth

aduth Aug 4, 2017

Member

And setting the src to a function?

@aduth

aduth Aug 4, 2017

Member

I did some testing. It seems to prevent onload callback if changed while in-flight. Though I think we'd ought to set to an empty string, not sure what the expected behavior of a function is.

image

blocks/library/image/index.js
+ lockAspectRatio
+ onResize={ ( event, { size } ) => setAttributes( size ) }
+ >
+ <img src={ url } alt={ alt } onClick={ setFocus } />
@aduth

aduth Aug 4, 2017

Member

Minor: We could create a single reference to this element to share between condition above and here:

diff --git a/blocks/library/image/index.js b/blocks/library/image/index.js
index f6985bc8..766fb8aa 100644
--- a/blocks/library/image/index.js
+++ b/blocks/library/image/index.js
@@ -178,8 +178,9 @@ registerBlockType( 'core/image', {
                        <figure key="image" className={ classes }>
                                <ImageSize src={ url }>
                                        { ( originalWidth = width, originalHeight = height ) => {
+                                               const img = <img src={ url } alt={ alt } onClick={ setFocus } />;
                                                if ( ! originalHeight || ! originalWidth ) {
-                                                       return <img src={ url } alt={ alt } onClick={ setFocus } />;
+                                                       return img;
                                                }
                                                return (
                                                        <ResizableBox
@@ -188,7 +189,7 @@ registerBlockType( 'core/image', {
                                                                lockAspectRatio
                                                                onResize={ ( event, { size } ) => setAttributes( size ) }
                                                        >
-                                                               <img src={ url } alt={ alt } onClick={ setFocus } />
+                                                               { img }
                                                        </ResizableBox>
                                                );
                                        } }
Contributor

mtias commented Aug 4, 2017

Pushed a change to account for resized images differently on floated instances (also centering on wider alignments).

image

The only slightly annoying thing is that, when the toolbar is wider than the image, it forces the width to be bigger:

image

When you unselect the image it goes back to the right size.

Contributor

mtias commented Aug 4, 2017

@jasmussen also maybe "wide" and "full-width" could clear the resized values.

Collaborator

jasmussen commented Aug 4, 2017

Vast improvements in a short amount of time, impressive. My hopes that we don't need to rewrite the full width math are restored.

Got some niggles still, though:

screen shot 2017-08-04 at 18 55 53

screen shot 2017-08-04 at 18 56 02

screen shot 2017-08-04 at 18 56 06

screen shot 2017-08-04 at 18 56 26

Collaborator

youknowriad commented Aug 4, 2017

Disabled the resizing for wide/full alignments but there're still some glitches for the left/right alignments.

Collaborator

youknowriad commented Aug 4, 2017 edited

Not sure I understand @mtias's fix but I found that always applying "data-resized" to the wrapper makes the component behave like I wanted.

Contributor

mtias commented Aug 4, 2017 edited

@youknowriad if the image is not resized we'd want the 370px max-width to apply when floated.

Collaborator

youknowriad commented Aug 4, 2017

@mtias I'm not sure this is true, floating means "only floating" for me and frontend styling doesn't include this max-width.

Contributor

mtias commented Aug 4, 2017

Yes, it hasn't been applied to front-end, and we may want to revise that, but that was the intention of having the max-width, because if the image is larger than the text column, it doesn't look floated at all.

Collaborator

youknowriad commented Aug 4, 2017 edited

@mtias What about setting the 370px width to the image attributes when we switch to the left/right alignment (if no size set before) and always set data-resized. This will fix both issues at once.

Contributor

mtias commented Aug 4, 2017

Let's try it.

Collaborator

youknowriad commented Aug 4, 2017

Ok updated, It works well IMO but there's one caveat: I had to add a 'resized' attribute to the image to differentiate between explicitely resized image and a size set automatically when applying float alignments. This boolean is used when moving to "center" alignment: if we explicitly resized the image, we keep the size unless we reset it.

Collaborator

youknowriad commented Aug 4, 2017

There's some small remaining issue where we set a right value in the width/height attributes but the component is not rerendered accordingly unless we refresh.

Collaborator

jasmussen commented Aug 4, 2017

There are a couple of issues still. But if we believe these are issues we can address, then it's worth getting this in as a first pass, and then iterating. If, on the other hand, any of these sound like we can't address them easily, might be worth rethinking.

  1. The resize handle should only be visible when the block is selected
  2. If you resize an image reeeeally tiny, the handle doesn't follow all the way down. And when you later size up, the handle is out of sync.
  3. It feels like we should do the same as the current editor does with regards to image widths, when floated. Applying a max width seems arbitrary. Maybe you WANT a giant image that's left aligned.
  4. When you size an image bigger up, you can invoke multi select if you move the cursor downwards. This is easiest to test while resizing a thin image, like the landscape image in the demo content.
  5. There seems to have been a regression with images in the post content that are wide and full wide. Then handles there are gone, as they should be, but if you set one of those wide images to "float left" or "float right" instead, you have to first resize them down from their giant wide size first. And so, if you are sizing down an image in the demo content from fullwide, we should probably set the main column width first.

2 and 4 seem like they can be fixed both, by applying mininum and maximum dimensions.

codecov bot commented Aug 4, 2017 edited

Codecov Report

Merging #2213 into master will decrease coverage by 0.06%.
The diff coverage is 6.52%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2213      +/-   ##
==========================================
- Coverage   25.35%   25.28%   -0.07%     
==========================================
  Files         151      152       +1     
  Lines        4701     4797      +96     
  Branches      792      816      +24     
==========================================
+ Hits         1192     1213      +21     
- Misses       2967     3026      +59     
- Partials      542      558      +16
Impacted Files Coverage Δ
blocks/with-editor-settings/index.js 60% <ø> (ø) ⬆️
editor/index.js 0% <0%> (ø) ⬆️
blocks/library/image/image-size.js 0% <0%> (ø)
blocks/library/image/index.js 12.24% <14.28%> (-2.47%) ⬇️
editor/effects.js 32.52% <0%> (+2.21%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b4dc3e0...1c30f9a. Read the comment docs.

Collaborator

youknowriad commented Aug 7, 2017

@jasmussen Updated with another approach that seems simpler and more robust. Mind taking a look?

Collaborator

jasmussen commented Aug 7, 2017

Love this feature, thank you for working on it.

Can't feel a material difference in the new approach, though.

I think we should consider looking at some min/max boundaries, and considering multi select triggering (I suspect multi select might also put cogs in the wheels of drag and drop down the road):

multiselect

floats

Collaborator

youknowriad commented Aug 7, 2017

I think we should consider looking at some min/max boundaries,

This is not hard to achieve but I don't think we should do it, themes have different max-width/height than the editor.

Multiselection

Not an easy problem to solve, I thought of a flag set to true in the state while resizing but it's not easily achievable because the blocks are technically not aware of the store (state) container (unless we continue our work of merging the blocks/editor modules). An option would be to provide startResizing, stopResizing as a prop to the edit function but obviously, this is not ideal. I suspect we'll have this multi-selection issue whenever a block uses drag and drop somehow (cc @iseulde @aduth )

Collaborator

jasmussen commented Aug 7, 2017

This is not hard to achieve but I don't think we should do it, themes have different max-width/height than the editor.

Wanted to clarify that I meant meant boundaries to resizing. Although it looks like there's no minimum size TinyMCE can resize images to, there's a max size. You can't size the image larger than the viewport.

resize

Contributor

mtias commented Aug 8, 2017

themes have different max-width/height than the editor.

This is something we'd like to address with the theme notifying the width of the main column to us. Widths that go beyond that should be done with the "wide" alignment, I think.

@mtias mtias added the Priority High label Aug 8, 2017

Collaborator

youknowriad commented Aug 9, 2017

Per slack discussion: I set the minimum width/height to 20px and the max-width to the image width.

Collaborator

jasmussen commented Aug 9, 2017

Thank you for working on this.

I'd love for the minimum dimensions to be 10px rather than 20px. But more importantly than those dimensions, I think, is that the handle detaches if you scale it smaller than what appears to be 20px height, and when you then scale up again, the handle sits in the air:

scaledown

Scaling up works 200% better now! It's almost perfect. But we may need scaling more scaling handles, or we're stuck when you make the image too big:

scaleup

It'd be nice (though not necessary for this PR) that the handles are hidden until you select the block.

For reference, here's what TinyMCE does:

tiny

Note how if you scale larger than the viewport, the sizing is discarded.

Collaborator

youknowriad commented Aug 9, 2017 edited

Note how if you scale larger than the viewport, the sizing is discarded.

TinyMCE limits scaling to the editor width and not the browser viewport. So, this seems more logic to me. For us, this might mean limit the scaling to the editor's content area size (which is 700px IIRC) but again, this means the user can't scale more than this even if it's theme allows it.

Also, the difference with TinyMCE is that Tinymce's size is flexible (it grows if you resize the window) which is not the case for our editor's content area.

I'm looking at the issues above

Collaborator

youknowriad commented Aug 9, 2017

the handle sits in the air:

This is fixed

It'd be nice (though not necessary for this PR) that the handles are hidden until you select the block.

This is fixed

But we may need more scaling handles

This can't be done with the current resizing library I'm using :(. Two options: find another library or write our own. The second one could be difficult to do. I'm looking at alternatives.

Collaborator

jasmussen commented Aug 9, 2017

TinyMCE limits scaling to the editor width and not the browser viewport. So, this seems more logic to me. For us, this might mean limit the scaling to the editor's content area size (which is 700px IIRC) but again, this means the user can't scale more than this even if it's theme allows it.

To be clear, it's fine to improvements in this area in separate non-milestoned PRs.

This can't be done with the current resizing library I'm using :(. Two options: find another library or write our own. The second one could be difficult to do. I'm looking at alternatives.

Bummer. Hmm. Can we choose which corner the handle sits on?

The big problem right now is that it's very easy to scale the image so large that the handle comes out of view, and then you can't scale it down. Perhaps we do need to limit the size to the editor width, so as to prevent this from happening.

Not saying it has to be perfect, looking for quick fixes here.

Collaborator

youknowriad commented Aug 9, 2017

@jasmussen Found a better library, let me know what do you think?

Collaborator

jasmussen commented Aug 9, 2017

🎉🎉🎉🎉

Oh my science this latest one is AMAZING! It feels completely solid! I think this can go in virtually as is!

A future improvement could be to improve this:

screen shot 2017-08-09 at 13 36 34

and this:

screen shot 2017-08-09 at 13 37 06

That is it'd be nice if the caption field was no larger than the image itself, and when the image is selected it seems like we might want to remove the 370px altogether on floats (which lets us remove the !important that Matías added).

The thing is, the max size on floated images is something we added because we didn't have a good resizing/insert thumbnail play. Now that we have good resizing, I don't think we need it.

Collaborator

youknowriad commented Aug 9, 2017

A future improvement could be to improve this:

Do you mean we should resize the whole "figure" node (image + caption) instead of resizing the image (current behaviour)? What do you suggest to fix those?

Collaborator

jasmussen commented Aug 9, 2017

Do you mean we should resize the whole "figure" node (image + caption) instead of resizing the image (current behaviour)? What do you suggest to fix those?

I'm not sure what the best way to improve this is. The idea would be that the caption field is no wider than the image. But let's explore this separately, and it doesn't have to be in the milestones.

Contributor

mtias commented Aug 9, 2017

For us, this might mean limit the scaling to the editor's content area size (which is 700px IIRC) but again, this means the user can't scale more than this even if it's theme allows it.

I think this is fine. If themes were to modify this, it would no longer be 700px in the editor.

youknowriad and others added some commits Aug 4, 2017

@youknowriad youknowriad Image Block: Adding image resizing handles ff5c623
@youknowriad youknowriad ImageBlock: Avoid the Image as a global and use window.Image 0793a71
@youknowriad youknowriad Image Block: Fix image-size unmounting behaviour and small tweaks d88fba1
@youknowriad youknowriad Image Block: disable resizing images if alignement is full or wide 829d986
@mtias @youknowriad mtias Handle resized images without forcing a max-width when floated. d5a408e
@youknowriad youknowriad Image Block: Change the width calculation mechanism to retrigger when…
… the alignment change
73347fe
@youknowriad youknowriad Image Block: Resizing is limited from 20px to the actual width/height…
… of the inserted image
12a26e1
@youknowriad youknowriad Image Block: Hide the resizing handle if the block is not selected 548e13f
@youknowriad youknowriad Image Block: Changing the resizing library to allow adding more resiz…
…ing handlers
06b88c3
@youknowriad youknowriad Image Block: Fix empty caption image
76798e8
Collaborator

youknowriad commented Aug 9, 2017

Not sure I understand, do you think this is ok to ship or is there anything else I should look at?

Contributor

mtias commented Aug 9, 2017

I meant not going beyond the width set on blocks that don't have wide/full alignment.

@youknowriad youknowriad Image Block: Constraint resizing within the editor canvas
1c30f9a
Collaborator

youknowriad commented Aug 9, 2017

👍 Understood and updated to constrain the resizing within the canvas. The value is hard-coded right now but we could easily make it overridable later.

Collaborator

jasmussen commented Aug 9, 2017

Love it, I think it's good to go!

We do definitely want ro revisit the max dimensions, but let's do that as part of a larger editor width discussion. I know there's a ticket for this separetely too. As it stands, this PR feels solid 👌

@youknowriad youknowriad merged commit 659b1bb into master Aug 9, 2017

3 checks passed

codecov/project 25.28% (-0.07%) compared to b4dc3e0
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details

@youknowriad youknowriad deleted the add/image-resize-handles branch Aug 9, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment