Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 128 additions & 4 deletions src/block-components/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ import { getAttributeName } from '~stackable/util'
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data'
import { useSelect, select } from '@wordpress/data'
import { _x, __ } from '@wordpress/i18n'
import { applyFilters } from '@wordpress/hooks'
import { useMemo } from '@wordpress/element'
import { useBlockEditContext } from '@wordpress/block-editor'

// Note: image drop shadows do not accept negative spread.
const IMAGE_SHADOWS = [
Expand All @@ -60,6 +61,7 @@ const Controls = props => {
imageHeightUnit: attributes.imageHeightUnit,
imageWidth: attributes.imageWidth,
imageHeight: attributes.imageHeight,
imageWidthAttribute: attributes.imageWidthAttribute,
imageHeightTablet: attributes[ getAttributeName( 'imageHeight', 'tablet' ) ],
imageHeightMobile: attributes[ getAttributeName( 'imageHeight', 'mobile' ) ],
imageHasLightbox: attributes.imageHasLightbox,
Expand All @@ -78,6 +80,24 @@ const Controls = props => {
const setAttributes = useBlockSetAttributesContext()
const deviceType = useDeviceType()

// Get the width of the image block, this is needed for resizing the image
// when replacing, and for resetting the width.
const { getEditorDom } = useSelect( 'stackable/editor-dom' )
const { clientId } = useBlockEditContext()
const editorDom = getEditorDom?.() || undefined
const isImageBlock = useMemo( () => {
return select( 'core/block-editor' ).getBlockName( clientId ) === 'stackable/image'
}, [ clientId ] )
const imageBlockWidth = useMemo( () => {
if ( editorDom ) {
if ( isImageBlock ) {
const blockEl = editorDom.querySelector( `[data-block="${ clientId }"]` )
return blockEl?.clientWidth || undefined
}
}
return undefined
}, [ editorDom, isImageBlock, clientId ] )

// Get the image size urls.
const { imageData } = useSelect( select => {
const image = select( 'core' ).getMedia( attributes.imageId )
Expand Down Expand Up @@ -117,6 +137,8 @@ const Controls = props => {
imageUrl: '',
imageWidthAttribute: '',
imageHeightAttribute: '',
imageWidthUnit: '',
imageHeightUnit: '',
} ) }
onChange={ image => {
// Get the URL of the currently selected image size.
Expand All @@ -131,14 +153,34 @@ const Controls = props => {
height = image.sizes?.[ currentSelectedSize ]?.height || height || ''
width = image.sizes?.[ currentSelectedSize ]?.width || width || ''
}
setAttributes( {

const newAttributes = {
imageId: image.id,
imageUrl: url,
imageWidthAttribute: width,
imageHeightAttribute: height,
imageExternalUrl: '',
...( attributes.imageAlt ? {} : { imageAlt: image.alt || '' } ), // Only set the alt if it's empty.
} )
}

// If the image being selected is smaller than the
// current width of the image block, don't use 100%
// because the image will look blurry, instead use the
// actual width.
if ( isImageBlock && imageBlockWidth && ! props.hasManuallyChangedDimensions ) {
// When the image gets reset, we need to also reset
// the width unit to '%' so that when we add another
// image, the image would not be small
newAttributes.imageWidth = ''
newAttributes.imageWidthUnit = '%'
// We need the width of the image block to compare
if ( width < imageBlockWidth ) {
newAttributes.imageWidth = width
newAttributes.imageWidthUnit = 'px'
}
}

setAttributes( newAttributes )
} }
/>
) }
Expand Down Expand Up @@ -200,8 +242,90 @@ const Controls = props => {
step={ props.widthStep }
initialPosition={ 100 }
allowReset={ true }
placeholder="250" // TODO: This should be referenced somewher instead of just a static number
// placeholder="250" // TODO: This should be referenced somewher instead of just a static number
placeholder="auto"
// Add a default value here so that the reset button will not appear.
default={ ( () => {
// We follow the logic in the override reset.
if ( isImageBlock && deviceType === 'Desktop' ) {
if ( attributes.imageWidthUnit === 'px' ) {
if ( imageBlockWidth && attributes.imageWidthAttribute < imageBlockWidth ) {
return attributes.imageWidthAttribute
}
}
}
return ''
} )() }
onChangeUnit={ ( unit, attributeName, oldUnit ) => {
// When the unit is changed, we need to adjust the width
// so that the image does not get distorted.
if ( isImageBlock && deviceType === 'Desktop' ) {
// Switching from % to px
if ( oldUnit === '%' && unit === 'px' ) {
// If image is too small, use the original image width
if ( imageBlockWidth && attributes.imageWidthAttribute < imageBlockWidth ) {
return setAttributes( {
imageWidth: attributes.imageWidthAttribute,
[ attributeName ]: unit,
} )
}
// If the width is larger than the block width, reset to 100% / width of block
if ( attributes.imageWidth === '' ) {
return setAttributes( {
imageWidth: imageBlockWidth,
[ attributeName ]: unit,
} )
}
// Switching from px to %
} else if ( oldUnit === 'px' && unit === '%' ) {
// If the image is larger than the block width, reset to 100%
if ( imageBlockWidth && attributes.imageWidthAttribute > imageBlockWidth ) {
return setAttributes( {
imageWidth: '',
[ attributeName ]: unit,
} )
}
// If image goes past 100$, reset to 100%
if ( attributes.imageWidth > 100 ) {
return setAttributes( {
imageWidth: '',
[ attributeName ]: unit,
} )
}
}
}
// Normal saving behavior.
setAttributes( { [ attributeName ]: unit } )
} }
responsive="all"
onOverrideReset={ () => {
// When resetting and in desktop, adjust the width so that we get the right "reset" value. Logic:
// - If the width is in px and the width attribute is set, use the original image with
// ...unless the image width is larger than the block width, then reset to 100%
// - If the width is in %, and the image is smaller than the block, reset to the 'px' width
// ...or just reset to 100%
if ( isImageBlock && deviceType === 'Desktop' ) {
let newWidthAttribute = ''
if ( attributes.imageWidthUnit === 'px' ) {
if ( attributes.imageWidthAttribute ) {
newWidthAttribute = attributes.imageWidthAttribute
}
if ( imageBlockWidth && attributes.imageWidthAttribute > imageBlockWidth ) {
newWidthAttribute = ''
// We need to do a 'set attribute' here.
setAttributes( { imageWidthUnit: '%' } )
}
} else if ( attributes.imageWidthUnit === '%' ) {
if ( imageBlockWidth && attributes.imageWidthAttribute < imageBlockWidth ) {
newWidthAttribute = attributes.imageWidthAttribute
// We need to do a 'set attribute' here.
setAttributes( { imageWidthUnit: 'px' } )
}
}
// Returning a value here overrides the reset into the new value
return newWidthAttribute
}
} }
helpTooltip={ {
//TODO: Add a working video
title: __( 'Image width', i18n ),
Expand Down
7 changes: 7 additions & 0 deletions src/block-components/image/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@
z-index: 1;
}
}
// make the placeholder occupy the entire area since the placeholder is used to
// measure in image blocks..
.stk-block-image {
.stk-img-wrapper.stk-img-placeholder {
min-width: 100%;
}
}

// Don't do the hover effect when adjusting the hover effect.
.stk-block:not(.stk--is-hovered) > .stk-img-wrapper {
Expand Down
34 changes: 32 additions & 2 deletions src/block-components/image/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import {
Button, Dashicon, ResizableBox,
} from '@wordpress/components'
import {
useState, useEffect, memo, useRef,
useState, useEffect, memo, useRef, useMemo,
} from '@wordpress/element'
import { select } from '@wordpress/data'
import { applyFilters } from '@wordpress/hooks'

const formSize = ( size = '', unit = '%', usePx = false, usePct = true ) => {
Expand Down Expand Up @@ -81,6 +82,12 @@ const Image = memo( props => {
const [ currentWidth, setCurrentWidth ] = useState()
const [ imageWidthIsTooSmall, setImageWidthIsTooSmall ] = useState( false )
const imageRef = useRef()
const wrapperRef = useRef()

const { clientId } = useBlockEditContext()
const isImageBlock = useMemo( () => {
return select( 'core/block-editor' ).getBlockName( clientId ) === 'stackable/image'
}, [ clientId ] )

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

// If the image being selected is smaller than the
// current width of the image block, don't use 100%
// because the image will look blurry, instead use the
// actual width.
if ( isImageBlock && imageRef.current && ! props.hasManuallyChangedDimensions ) {
// When the image gets reset, we need to also reset
// the width unit to '' so that when we add another
// image, the image would not be small
props.onChangeSize( {
width: '',
widthUnit: '%',
} )
// We need the width of the image block to compare
const imageBlockWidth = wrapperRef.current.parentElement?.parentElement?.clientWidth ||
wrapperRef.current.clientWidth
if ( width < imageBlockWidth ) {
props.onChangeSize( {
width,
widthUnit: 'px',
} )
}
}

props.onChange( {
...image,
url,
Expand Down
20 changes: 17 additions & 3 deletions src/block/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import { __ } from '@wordpress/i18n'
import { compose } from '@wordpress/compose'
import { useBlockEditContext } from '@wordpress/block-editor'
import { applyFilters, addFilter } from '@wordpress/hooks'
import { memo } from '@wordpress/element'
import {
memo, useState, useEffect,
} from '@wordpress/element'
import { useSelect } from '@wordpress/data'

const heightUnit = [ 'px', 'vh', '%' ]
Expand All @@ -55,7 +57,6 @@ const Edit = props => {
const figcaptionClassnames = classnames(
getTypographyClasses( props.attributes, 'figcaption%s' ),
'stk-img-figcaption'

)

const blockAlignmentClass = getAlignmentClasses( props.attributes )
Expand All @@ -78,6 +79,14 @@ const Edit = props => {
blockAlignmentClass,
] )

// This is used to track whether or not the user has manually changed the
// dimensions of the image. If not, then when the user changes the image
// size, the dimensions will be automatically calculated.
const [ hasManuallyChangedDimensions, setHasManuallyChangedDimensions ] = useState( !! props.attributes.imageWidth )
useEffect( () => {
setHasManuallyChangedDimensions( !! props.attributes.imageWidth )
}, [ props.attributes.imageWidth ] )

// Generate the CSS styles for the block.
const blockCss = useBlockCssGenerator( {
attributes: props.attributes,
Expand All @@ -91,7 +100,10 @@ const Edit = props => {

return (
<>
<InspectorControls enableLink={ enableLink } />
<InspectorControls
enableLink={ enableLink }
hasManuallyChangedDimensions={ hasManuallyChangedDimensions }
/>

{ blockCss && <style key="block-css">{ blockCss }</style> }
<CustomCSS mainBlockClass="stk-block-image" />
Expand All @@ -107,6 +119,7 @@ const Edit = props => {
heightUnits={ heightUnit }
defaultWidth="100"
defaultHeight="auto"
hasManuallyChangedDimensions={ hasManuallyChangedDimensions }
/>
{ props.attributes.figcaptionShow &&
<Typography
Expand All @@ -132,6 +145,7 @@ const InspectorControls = memo( props => {
initialOpen={ true }
heightUnits={ heightUnit }
hasLightbox
hasManuallyChangedDimensions={ props.hasManuallyChangedDimensions }
/>
{ props.enableLink && <Link.InspectorControls hasTitle={ true } isAdvancedTab={ true } /> }
<BlockDiv.InspectorControls />
Expand Down
12 changes: 11 additions & 1 deletion src/components/advanced-range-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,16 @@ const AdvancedRangeControl = props => {
// Important, the attribute type for this option should be a string.
const _onChange = value => {
const onChangeFunc = typeof props.onChange === 'undefined' ? onChange : props.onChange
onChangeFunc( props.isDynamic ? value.toString() : value )
let newValue = props.isDynamic ? value.toString() : value

// On reset, allow overriding the value.
if ( newValue === '' ) {
const overrideValue = props.onOverrideReset?.()
if ( typeof overrideValue !== 'undefined' ) {
newValue = overrideValue
}
}
onChangeFunc( newValue )
}

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

value: undefined,
onChange: undefined,
onOverrideReset: undefined,
forcePlaceholder: false,
}

Expand Down
9 changes: 7 additions & 2 deletions src/components/base-control2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,18 @@ const AdvancedControl = props => {

const unit = props.unit ? props.unit : unitAttribute
const setAttributes = useBlockSetAttributesContext()
const onChangeUnit = unit => setAttributes( { [ unitAttrName ]: unit } )
const onChangeUnit = unit => {
if ( props.onChangeUnit ) {
return props.onChangeUnit( unit, unitAttrName, unitAttribute )
}
setAttributes( { [ unitAttrName ]: unit } )
}

return (
<BaseControl
{ ...props }
unit={ unit }
onChangeUnit={ props.onChangeUnit || onChangeUnit }
onChangeUnit={ onChangeUnit }
/>
)
}
Expand Down
Loading