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

Image Block - Fix scaling image outside block parent in editor. #19541

89 changes: 61 additions & 28 deletions packages/block-library/src/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
__experimentalImageSizeControl as ImageSizeControl,
__experimentalImageURLInputUI as ImageURLInputUI,
} from '@wordpress/block-editor';
import { Component } from '@wordpress/element';
import { Component, createRef } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { getPath } from '@wordpress/url';
import { withViewportMatch } from '@wordpress/viewport';
Expand Down Expand Up @@ -100,9 +100,18 @@ export class ImageEdit extends Component {
this.getFilename = this.getFilename.bind( this );
this.onUploadError = this.onUploadError.bind( this );
this.onImageError = this.onImageError.bind( this );
this.updateMaxWidth = this.updateMaxWidth.bind( this );
// With the current implementation of ResizableBox, an image needs an explicit pixel value for the max-width.
// In absence of being able to set the content-width, this max-width is currently dictated by the vanilla editor style.
// The following variable adds a buffer to this vanilla style, to create a large buffer until updateMaxWidth is called.
this.maxWidthBuffer = this.props.maxWidth * 2.5;
// To limit scaling the image beyond the width of the main column, we will use resizeRef to find the parent block's width.
// This will then be used to reduce the maxWidth from the buffer value to a definitive value in componentDidUpdate.
this.resizeRef = createRef();

this.state = {
captionFocused: false,
maxWidth: this.maxWidthBuffer,
};
}

Expand All @@ -126,6 +135,21 @@ export class ImageEdit extends Component {
} );
}
}

// Set updates for maxWidth once resizeRef is ready.
const waitForRef = setInterval( () => {
if ( this.resizeRef.current ) {
this.updateMaxWidth();
// Listener will update maxWidth on resize for fluid-width themes.
window.addEventListener( 'resize', this.updateMaxWidth );
clearInterval( waitForRef );
}
}, 100 );
}

componentWillUnmount() {
// Remove listener to update maxWidth on resize.
window.removeEventListener( 'resize', this.updateMaxWidth );
}

componentDidUpdate( prevProps ) {
Expand All @@ -150,6 +174,23 @@ export class ImageEdit extends Component {
}
}

// Required to set maxWidth based on block parent.
updateMaxWidth() {
// Check resizeRef's block parents to set an accurate width restriction.
// This is used to limit the resizer from resizing the image beyond the block parent's width.
if ( this.resizeRef.current ) {
let parentBlock = this.resizeRef.current.parentNode;
// Traverse 'up' in parent nodes until we find the block parent.
while ( ! parentBlock.classList.contains( 'block-editor-block-list__block' ) ) {
parentBlock = parentBlock.parentNode;
}
// Only setState if maxWidth value has changed.
if ( this.state.maxWidth !== parentBlock.offsetWidth ) {
this.setState( { maxWidth: parentBlock.offsetWidth } );
}
}
}

onUploadError( message ) {
const { noticeOperations } = this.props;
noticeOperations.removeAllNotices();
Expand Down Expand Up @@ -328,12 +369,12 @@ export class ImageEdit extends Component {
isLargeViewport,
isSelected,
className,
maxWidth,
noticeUI,
isRTL,
onResizeStart,
onResizeStop,
} = this.props;
const { maxWidth } = this.state;
const {
url,
alt,
Expand Down Expand Up @@ -578,13 +619,6 @@ export class ImageEdit extends Component {
? MIN_SIZE
: MIN_SIZE / ratio;

// With the current implementation of ResizableBox, an image needs an explicit pixel value for the max-width.
// In absence of being able to set the content-width, this max-width is currently dictated by the vanilla editor style.
// The following variable adds a buffer to this vanilla style, so 3rd party themes have some wiggleroom.
// This does, in most cases, allow you to scale the image beyond the width of the main column, though not infinitely.
// @todo It would be good to revisit this once a content-width variable becomes available.
const maxWidthBuffer = maxWidth * 2.5;

let showRightHandle = false;
let showLeftHandle = false;

Expand Down Expand Up @@ -615,20 +649,17 @@ export class ImageEdit extends Component {
/* eslint-enable no-lonely-if */

return (
<>
{ getInspectorControls(
imageWidth,
imageHeight
) }
<div ref={ this.resizeRef } >
{ getInspectorControls( imageWidth, imageHeight ) }
<ResizableBox
size={ {
width,
height,
} }
minWidth={ minWidth }
maxWidth={ maxWidthBuffer }
maxWidth={ maxWidth }
minHeight={ minHeight }
maxHeight={ maxWidthBuffer / ratio }
maxHeight={ maxWidth / ratio }
lockAspectRatio
enable={ {
top: false,
Expand All @@ -644,22 +675,24 @@ export class ImageEdit extends Component {
delta
) => {
onResizeStop();
setAttributes( {
width: parseInt(
currentWidth + delta.width,
10
),
height: parseInt(
currentHeight +
delta.height,
10
),
} );
// Images saved with width (currentWidth) larger than the width of their parent block (maxWidth),
// will resize with poor behavior if we do not resize based on maxWidth in that circumstance.
if ( currentWidth > maxWidth ) {
setAttributes( {
width: parseInt( maxWidth + delta.width, 10 ),
height: parseInt( ( maxWidth / ratio ) + delta.height, 10 ),
} );
} else {
setAttributes( {
width: parseInt( currentWidth + delta.width, 10 ),
height: parseInt( currentHeight + delta.height, 10 ),
} );
}
} }
>
{ img }
</ResizableBox>
</>
</div>
);
} }
</ImageSize>
Expand Down
8 changes: 8 additions & 0 deletions packages/block-library/src/image/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@
z-index: z-index(".block-library-image__resize-handlers");
}

// Ensures image ratio is respected on resizing viewport.
.block-editor-block-list__block[data-type="core/image"]
.wp-block-image
.components-resizable-box__container
img {
height: auto;
}

.block-editor-block-list__block[data-type="core/image"][data-align="center"] {
.wp-block-image {
margin-left: auto;
Expand Down