diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index 7c8b2c2715c99..fad94a62e5a75 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -64,10 +64,10 @@ "__experimentalRole": "content" }, "width": { - "type": "number" + "type": "string" }, "height": { - "type": "number" + "type": "string" }, "aspectRatio": { "type": "string" diff --git a/packages/block-library/src/image/deprecated.js b/packages/block-library/src/image/deprecated.js index edbabd7fb2d83..d159009b17373 100644 --- a/packages/block-library/src/image/deprecated.js +++ b/packages/block-library/src/image/deprecated.js @@ -740,4 +740,211 @@ const v6 = { }, }; -export default [ v6, v5, v4, v3, v2, v1 ]; +/** + * Deprecation for converting to string width and height block attributes and + * removing the width and height img element attributes which are not needed + * as they get added by the TODO hook. + * + * @see https://github.com/WordPress/gutenberg/pull/53274 + */ +const v7 = { + attributes: { + align: { + type: 'string', + }, + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + __experimentalRole: 'content', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + __experimentalRole: 'content', + }, + caption: { + type: 'string', + source: 'html', + selector: 'figcaption', + __experimentalRole: 'content', + }, + title: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'title', + __experimentalRole: 'content', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'href', + __experimentalRole: 'content', + }, + rel: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'rel', + }, + linkClass: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'class', + }, + id: { + type: 'number', + __experimentalRole: 'content', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, + aspectRatio: { + type: 'string', + }, + scale: { + type: 'string', + }, + sizeSlug: { + type: 'string', + }, + linkDestination: { + type: 'string', + }, + linkTarget: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'target', + }, + }, + supports: { + anchor: true, + behaviors: { + lightbox: true, + }, + color: { + text: false, + background: false, + }, + filter: { + duotone: true, + }, + __experimentalBorder: { + color: true, + radius: true, + width: true, + __experimentalSkipSerialization: true, + __experimentalDefaultControls: { + color: true, + radius: true, + width: true, + }, + }, + }, + migrate( { width, height, ...attributes } ) { + return { + ...attributes, + width: `${ width }px`, + height: `${ height }px`, + }; + }, + save( { attributes } ) { + const { + url, + alt, + caption, + align, + href, + rel, + linkClass, + width, + height, + aspectRatio, + scale, + id, + linkTarget, + sizeSlug, + title, + } = attributes; + + const newRel = ! rel ? undefined : rel; + const borderProps = getBorderClassesAndStyles( attributes ); + + const classes = classnames( { + [ `align${ align }` ]: align, + [ `size-${ sizeSlug }` ]: sizeSlug, + 'is-resized': width || height, + 'has-custom-border': + !! borderProps.className || + ( borderProps.style && + Object.keys( borderProps.style ).length > 0 ), + } ); + + const imageClasses = classnames( borderProps.className, { + [ `wp-image-${ id }` ]: !! id, + } ); + + const image = ( + { + ); + + const figure = ( + <> + { href ? ( + + { image } + + ) : ( + image + ) } + { ! RichText.isEmpty( caption ) && ( + + ) } + + ); + + return ( +
+ { figure } +
+ ); + }, +}; + +export default [ v7, v6, v5, v4, v3, v2, v1 ]; diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 5fe5c93f4199b..a880c20ed59ab 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -114,6 +114,11 @@ export default function Image( { linkTarget, sizeSlug, } = attributes; + + // The only supported unit is px, so we can parseInt to strip the px here. + const numericWidth = width ? parseInt( width, 10 ) : undefined; + const numericHeight = height ? parseInt( height, 10 ) : undefined; + const imageRef = useRef(); const prevCaption = usePrevious( caption ); const [ showCaption, setShowCaption ] = useState( !! caption ); @@ -473,23 +478,14 @@ export default function Image( { ) } { // Rebuilding the object forces setting `undefined` // for values that are removed since setAttributes // doesn't do anything with keys that aren't set. setAttributes( { - width: - newValue.width && - parseInt( newValue.width, 10 ), - height: - newValue.height && - parseInt( newValue.height, 10 ), + width: newValue.width, + height: newValue.height, scale: newValue.scale, aspectRatio: newValue.aspectRatio, } ); @@ -587,8 +583,8 @@ export default function Image( { { img }; } else { + const numericRatio = aspectRatio && evalAspectRatio( aspectRatio ); + const customRatio = numericWidth / numericHeight; const ratio = - ( aspectRatio && evalAspectRatio( aspectRatio ) ) || - ( width && height && width / height ) || - naturalWidth / naturalHeight || - 1; - - const currentWidth = ! width && height ? height * ratio : width; - const currentHeight = ! height && width ? width / ratio : height; + numericRatio || customRatio || naturalWidth / naturalHeight || 1; + const currentWidth = + ! numericWidth && numericHeight + ? numericHeight * ratio + : numericWidth; + const currentHeight = + ! numericHeight && numericWidth + ? numericWidth / ratio + : numericHeight; const minWidth = naturalWidth < naturalHeight ? MIN_SIZE : MIN_SIZE * ratio; @@ -687,10 +687,14 @@ export default function Image( { onResizeStart={ onResizeStart } onResizeStop={ ( event, direction, elt ) => { onResizeStop(); + // Since the aspect ratio is locked when resizing, we can + // use the width of the resized element to calculate the + // height in CSS to prevent stretching when the max-width + // is reached. setAttributes( { - width: elt.offsetWidth, - height: elt.offsetHeight, - aspectRatio: undefined, + width: `${ elt.offsetWidth }px`, + height: 'auto', + aspectRatio: `${ ratio }`, } ); } } resizeRatio={ align === 'center' ? 2 : 1 } diff --git a/packages/block-library/src/image/save.js b/packages/block-library/src/image/save.js index 6fa8c6b2342f3..81565af09abab 100644 --- a/packages/block-library/src/image/save.js +++ b/packages/block-library/src/image/save.js @@ -61,8 +61,6 @@ export default function save( { attributes } ) { width, height, } } - width={ width } - height={ height } title={ title } /> ); diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html index 7ce56e11fa75e..9a66da6c01898 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html index 7ce56e11fa75e..9a66da6c01898 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html index cfdc52e3cbb6e..99da2155bce88 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html index ad70fe65f3582..807ba3abc9f9c 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html new file mode 100644 index 0000000000000..7210aee3564b5 --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json new file mode 100644 index 0000000000000..b49c515f8e5ab --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/image", + "isValid": true, + "attributes": { + "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==", + "alt": "", + "caption": "", + "sizeSlug": "large", + "width": "164px", + "height": "164px" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json new file mode 100644 index 0000000000000..fbd8627807b3d --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json @@ -0,0 +1,15 @@ +[ + { + "blockName": "core/image", + "attrs": { + "width": 164, + "height": 164, + "sizeSlug": "large" + }, + "innerBlocks": [], + "innerHTML": "\n
\"\"
\n", + "innerContent": [ + "\n
\"\"
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html new file mode 100644 index 0000000000000..5392d261af89d --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html @@ -0,0 +1,3 @@ + +
+