Skip to content

Commit 8b01521

Browse files
fix (image block): using small width images will not stretch the image to 100%
1 parent 558247b commit 8b01521

File tree

5 files changed

+154
-10
lines changed

5 files changed

+154
-10
lines changed

src/block-components/image/edit.js

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ import { getAttributeName } from '~stackable/util'
3232
/**
3333
* WordPress dependencies
3434
*/
35-
import { useSelect } from '@wordpress/data'
35+
import { useSelect, select } from '@wordpress/data'
3636
import { _x, __ } from '@wordpress/i18n'
3737
import { applyFilters } from '@wordpress/hooks'
3838
import { useMemo } from '@wordpress/element'
39+
import { useBlockEditContext } from '@wordpress/block-editor'
3940

4041
// Note: image drop shadows do not accept negative spread.
4142
const IMAGE_SHADOWS = [
@@ -60,6 +61,7 @@ const Controls = props => {
6061
imageHeightUnit: attributes.imageHeightUnit,
6162
imageWidth: attributes.imageWidth,
6263
imageHeight: attributes.imageHeight,
64+
imageWidthAttribute: attributes.imageWidthAttribute,
6365
imageHeightTablet: attributes[ getAttributeName( 'imageHeight', 'tablet' ) ],
6466
imageHeightMobile: attributes[ getAttributeName( 'imageHeight', 'mobile' ) ],
6567
imageHasLightbox: attributes.imageHasLightbox,
@@ -78,6 +80,24 @@ const Controls = props => {
7880
const setAttributes = useBlockSetAttributesContext()
7981
const deviceType = useDeviceType()
8082

83+
// Get the width of the image block, this is needed for resizing the image
84+
// when replacing, and for resetting the width.
85+
const { getEditorDom } = useSelect( 'stackable/editor-dom' )
86+
const { clientId } = useBlockEditContext()
87+
const editorDom = getEditorDom?.() || undefined
88+
const isImageBlock = useMemo( () => {
89+
return select( 'core/block-editor' ).getBlockName( clientId ) === 'stackable/image'
90+
}, [ clientId ] )
91+
const imageBlockWidth = useMemo( () => {
92+
if ( editorDom ) {
93+
if ( isImageBlock ) {
94+
const blockEl = editorDom.querySelector( `[data-block="${ clientId }"]` )
95+
return blockEl?.clientWidth || undefined
96+
}
97+
}
98+
return undefined
99+
}, [ editorDom, isImageBlock, clientId ] )
100+
81101
// Get the image size urls.
82102
const { imageData } = useSelect( select => {
83103
const image = select( 'core' ).getMedia( attributes.imageId )
@@ -117,6 +137,8 @@ const Controls = props => {
117137
imageUrl: '',
118138
imageWidthAttribute: '',
119139
imageHeightAttribute: '',
140+
imageWidthUnit: '',
141+
imageHeightUnit: '',
120142
} ) }
121143
onChange={ image => {
122144
// Get the URL of the currently selected image size.
@@ -131,14 +153,34 @@ const Controls = props => {
131153
height = image.sizes?.[ currentSelectedSize ]?.height || height || ''
132154
width = image.sizes?.[ currentSelectedSize ]?.width || width || ''
133155
}
134-
setAttributes( {
156+
157+
const newAttributes = {
135158
imageId: image.id,
136159
imageUrl: url,
137160
imageWidthAttribute: width,
138161
imageHeightAttribute: height,
139162
imageExternalUrl: '',
140163
...( attributes.imageAlt ? {} : { imageAlt: image.alt || '' } ), // Only set the alt if it's empty.
141-
} )
164+
}
165+
166+
// If the image being selected is smaller than the
167+
// current width of the image block, don't use 100%
168+
// because the image will look blurry, instead use the
169+
// actual width.
170+
if ( isImageBlock && imageBlockWidth && ! props.hasManuallyChangedDimensions ) {
171+
// When the image gets reset, we need to also reset
172+
// the width unit to '%' so that when we add another
173+
// image, the image would not be small
174+
newAttributes.imageWidth = ''
175+
newAttributes.imageWidthUnit = '%'
176+
// We need the width of the image block to compare
177+
if ( width < imageBlockWidth ) {
178+
newAttributes.imageWidth = width
179+
newAttributes.imageWidthUnit = 'px'
180+
}
181+
}
182+
183+
setAttributes( newAttributes )
142184
} }
143185
/>
144186
) }
@@ -200,8 +242,49 @@ const Controls = props => {
200242
step={ props.widthStep }
201243
initialPosition={ 100 }
202244
allowReset={ true }
203-
placeholder="250" // TODO: This should be referenced somewher instead of just a static number
245+
// placeholder="250" // TODO: This should be referenced somewher instead of just a static number
246+
placeholder="auto"
247+
// Add a default value here so that the reset button will not appear.
248+
default={ ( () => {
249+
// We follow the logic in the override reset.
250+
if ( isImageBlock && deviceType === 'Desktop' ) {
251+
if ( attributes.imageWidthUnit === 'px' ) {
252+
if ( imageBlockWidth && attributes.imageWidthAttribute < imageBlockWidth ) {
253+
return attributes.imageWidthAttribute
254+
}
255+
}
256+
}
257+
return ''
258+
} )() }
204259
responsive="all"
260+
onOverrideReset={ () => {
261+
// When resetting and in desktop, adjust the width so that we get the right "reset" value. Logic:
262+
// - If the width is in px and the width attribute is set, use the original image with
263+
// ...unless the image width is larger than the block width, then reset to 100%
264+
// - If the width is in %, and the image is smaller than the block, reset to the 'px' width
265+
// ...or just reset to 100%
266+
if ( isImageBlock && deviceType === 'Desktop' ) {
267+
let newWidthAttribute = ''
268+
if ( attributes.imageWidthUnit === 'px' ) {
269+
if ( attributes.imageWidthAttribute ) {
270+
newWidthAttribute = attributes.imageWidthAttribute
271+
}
272+
if ( imageBlockWidth && attributes.imageWidthAttribute > imageBlockWidth ) {
273+
newWidthAttribute = ''
274+
// We need to do a 'set attribute' here.
275+
setAttributes( { imageWidthUnit: '%' } )
276+
}
277+
} else if ( attributes.imageWidthUnit === '%' ) {
278+
if ( imageBlockWidth && attributes.imageWidthAttribute < imageBlockWidth ) {
279+
newWidthAttribute = attributes.imageWidthAttribute
280+
// We need to do a 'set attribute' here.
281+
setAttributes( { imageWidthUnit: 'px' } )
282+
}
283+
}
284+
// Returning a value here overrides the reset into the new value
285+
return newWidthAttribute
286+
}
287+
} }
205288
helpTooltip={ {
206289
//TODO: Add a working video
207290
title: __( 'Image width', i18n ),

src/block-components/image/editor.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,13 @@
232232
z-index: 1;
233233
}
234234
}
235+
// make the placeholder occupy the entire area since the placeholder is used to
236+
// measure in image blocks..
237+
.stk-block-image {
238+
.stk-img-wrapper.stk-img-placeholder {
239+
min-width: 100%;
240+
}
241+
}
235242

236243
// Don't do the hover effect when adjusting the hover effect.
237244
.stk-block:not(.stk--is-hovered) > .stk-img-wrapper {

src/block-components/image/image.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import {
2323
Button, Dashicon, ResizableBox,
2424
} from '@wordpress/components'
2525
import {
26-
useState, useEffect, memo, useRef,
26+
useState, useEffect, memo, useRef, useMemo,
2727
} from '@wordpress/element'
28+
import { select } from '@wordpress/data'
2829
import { applyFilters } from '@wordpress/hooks'
2930

3031
const formSize = ( size = '', unit = '%', usePx = false, usePct = true ) => {
@@ -81,6 +82,12 @@ const Image = memo( props => {
8182
const [ currentWidth, setCurrentWidth ] = useState()
8283
const [ imageWidthIsTooSmall, setImageWidthIsTooSmall ] = useState( false )
8384
const imageRef = useRef()
85+
const wrapperRef = useRef()
86+
87+
const { clientId } = useBlockEditContext()
88+
const isImageBlock = useMemo( () => {
89+
return select( 'core/block-editor' ).getBlockName( clientId ) === 'stackable/image'
90+
}, [ clientId ] )
8491

8592
// Used to fix issue with Resizable where height in % doesn't show while resizing.
8693
// @see https://github.com/bokuweb/re-resizable/issues/442
@@ -262,7 +269,7 @@ const Image = memo( props => {
262269
} }
263270
/>
264271
) }
265-
<div className="stk-img-resizer-wrapper">
272+
<div className="stk-img-resizer-wrapper" ref={ wrapperRef }>
266273
<img
267274
ref={ imageRef }
268275
onLoad={ () => setHasImageError( false ) }
@@ -293,6 +300,29 @@ const Image = memo( props => {
293300
height = image.sizes[ currentSelectedSize ].height
294301
}
295302

303+
// If the image being selected is smaller than the
304+
// current width of the image block, don't use 100%
305+
// because the image will look blurry, instead use the
306+
// actual width.
307+
if ( isImageBlock && imageRef.current && ! props.hasManuallyChangedDimensions ) {
308+
// When the image gets reset, we need to also reset
309+
// the width unit to '' so that when we add another
310+
// image, the image would not be small
311+
props.onChangeSize( {
312+
width: '',
313+
widthUnit: '%',
314+
} )
315+
// We need the width of the image block to compare
316+
const imageBlockWidth = wrapperRef.current.parentElement?.parentElement?.clientWidth ||
317+
wrapperRef.current.clientWidth
318+
if ( width < imageBlockWidth ) {
319+
props.onChangeSize( {
320+
width,
321+
widthUnit: 'px',
322+
} )
323+
}
324+
}
325+
296326
props.onChange( {
297327
...image,
298328
url,

src/block/image/edit.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ import { __ } from '@wordpress/i18n'
4141
import { compose } from '@wordpress/compose'
4242
import { useBlockEditContext } from '@wordpress/block-editor'
4343
import { applyFilters, addFilter } from '@wordpress/hooks'
44-
import { memo } from '@wordpress/element'
44+
import {
45+
memo, useState, useEffect,
46+
} from '@wordpress/element'
4547
import { useSelect } from '@wordpress/data'
4648

4749
const heightUnit = [ 'px', 'vh', '%' ]
@@ -55,7 +57,6 @@ const Edit = props => {
5557
const figcaptionClassnames = classnames(
5658
getTypographyClasses( props.attributes, 'figcaption%s' ),
5759
'stk-img-figcaption'
58-
5960
)
6061

6162
const blockAlignmentClass = getAlignmentClasses( props.attributes )
@@ -78,6 +79,14 @@ const Edit = props => {
7879
blockAlignmentClass,
7980
] )
8081

82+
// This is used to track whether or not the user has manually changed the
83+
// dimensions of the image. If not, then when the user changes the image
84+
// size, the dimensions will be automatically calculated.
85+
const [ hasManuallyChangedDimensions, setHasManuallyChangedDimensions ] = useState( !! props.attributes.imageWidth )
86+
useEffect( () => {
87+
setHasManuallyChangedDimensions( !! props.attributes.imageWidth )
88+
}, [ props.attributes.imageWidth ] )
89+
8190
// Generate the CSS styles for the block.
8291
const blockCss = useBlockCssGenerator( {
8392
attributes: props.attributes,
@@ -91,7 +100,10 @@ const Edit = props => {
91100

92101
return (
93102
<>
94-
<InspectorControls enableLink={ enableLink } />
103+
<InspectorControls
104+
enableLink={ enableLink }
105+
hasManuallyChangedDimensions={ hasManuallyChangedDimensions }
106+
/>
95107

96108
{ blockCss && <style key="block-css">{ blockCss }</style> }
97109
<CustomCSS mainBlockClass="stk-block-image" />
@@ -107,6 +119,7 @@ const Edit = props => {
107119
heightUnits={ heightUnit }
108120
defaultWidth="100"
109121
defaultHeight="auto"
122+
hasManuallyChangedDimensions={ hasManuallyChangedDimensions }
110123
/>
111124
{ props.attributes.figcaptionShow &&
112125
<Typography
@@ -132,6 +145,7 @@ const InspectorControls = memo( props => {
132145
initialOpen={ true }
133146
heightUnits={ heightUnit }
134147
hasLightbox
148+
hasManuallyChangedDimensions={ props.hasManuallyChangedDimensions }
135149
/>
136150
{ props.enableLink && <Link.InspectorControls hasTitle={ true } isAdvancedTab={ true } /> }
137151
<BlockDiv.InspectorControls />

src/components/advanced-range-control/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,16 @@ const AdvancedRangeControl = props => {
102102
// Important, the attribute type for this option should be a string.
103103
const _onChange = value => {
104104
const onChangeFunc = typeof props.onChange === 'undefined' ? onChange : props.onChange
105-
onChangeFunc( props.isDynamic ? value.toString() : value )
105+
let newValue = props.isDynamic ? value.toString() : value
106+
107+
// On reset, allow overriding the value.
108+
if ( newValue === '' ) {
109+
const overrideValue = props.onOverrideReset?.()
110+
if ( typeof overrideValue !== 'undefined' ) {
111+
newValue = overrideValue
112+
}
113+
}
114+
onChangeFunc( newValue )
106115
}
107116

108117
const derivedValue = typeof props.value === 'undefined' ? value : props.value
@@ -148,6 +157,7 @@ AdvancedRangeControl.defaultProps = {
148157

149158
value: undefined,
150159
onChange: undefined,
160+
onOverrideReset: undefined,
151161
forcePlaceholder: false,
152162
}
153163

0 commit comments

Comments
 (0)