diff --git a/packages/block-editor/src/components/image-editor/cropper.js b/packages/block-editor/src/components/image-editor/cropper.js
index c6a005551573be..ed0be17a09b154 100644
--- a/packages/block-editor/src/components/image-editor/cropper.js
+++ b/packages/block-editor/src/components/image-editor/cropper.js
@@ -23,6 +23,7 @@ export default function ImageCropper( {
clientWidth,
naturalHeight,
naturalWidth,
+ borderProps,
} ) {
const {
isInProgress,
@@ -44,10 +45,15 @@ export default function ImageCropper( {
return (
div,
> a {
@@ -63,7 +67,12 @@ figure.wp-block-gallery.has-nested-images {
}
}
- &.is-style-rounded {
+ &.has-custom-border img {
+ box-sizing: border-box;
+ }
+
+ &.is-style-rounded,
+ &.has-custom-border {
> div,
> a {
flex: 1 1 auto;
diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json
index 25492061048214..ba19aaa9d954d7 100644
--- a/packages/block-library/src/image/block.json
+++ b/packages/block-library/src/image/block.json
@@ -84,9 +84,15 @@
"background": false
},
"__experimentalBorder": {
+ "color": true,
"radius": true,
+ "width": true,
+ "__experimentalSelector": "img, .wp-block-image__crop-area",
+ "__experimentalSkipSerialization": true,
"__experimentalDefaultControls": {
- "radius": true
+ "color": true,
+ "radius": true,
+ "width": true
}
},
"__experimentalStyle": {
diff --git a/packages/block-library/src/image/deprecated.js b/packages/block-library/src/image/deprecated.js
index 338eac670c5320..9f5aaee4f7d21a 100644
--- a/packages/block-library/src/image/deprecated.js
+++ b/packages/block-library/src/image/deprecated.js
@@ -31,6 +31,12 @@ const blockAttributes = {
source: 'html',
selector: 'figcaption',
},
+ title: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'img',
+ attribute: 'title',
+ },
href: {
type: 'string',
source: 'attribute',
@@ -58,6 +64,9 @@ const blockAttributes = {
height: {
type: 'number',
},
+ sizeSlug: {
+ type: 'string',
+ },
linkDestination: {
type: 'string',
},
@@ -85,6 +94,83 @@ const blockSupports = {
};
const deprecated = [
+ // The following deprecation moves existing border radius styles onto the
+ // inner img element where new border block support styles must be applied.
+ // It will also add a new `.has-custom-border` class for existing blocks
+ // with border radii set. This class is required to improve caption position
+ // and styling when an image within a gallery has a custom border or
+ // rounded corners.
+ //
+ // See: https://github.com/WordPress/gutenberg/pull/31366/
+ {
+ attributes: blockAttributes,
+ supports: blockSupports,
+ save( { attributes } ) {
+ const {
+ url,
+ alt,
+ caption,
+ align,
+ href,
+ rel,
+ linkClass,
+ width,
+ height,
+ id,
+ linkTarget,
+ sizeSlug,
+ title,
+ } = attributes;
+
+ const newRel = isEmpty( rel ) ? undefined : rel;
+
+ const classes = classnames( {
+ [ `align${ align }` ]: align,
+ [ `size-${ sizeSlug }` ]: sizeSlug,
+ 'is-resized': width || height,
+ } );
+
+ const image = (
+
+ );
+
+ const figure = (
+ <>
+ { href ? (
+
+ { image }
+
+ ) : (
+ image
+ ) }
+ { ! RichText.isEmpty( caption ) && (
+
+ ) }
+ >
+ );
+
+ return (
+
+ { figure }
+
+ );
+ },
+ },
{
attributes: {
...blockAttributes,
diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js
index 3af2bfe7deffa4..7b426be38e6d85 100644
--- a/packages/block-library/src/image/edit.js
+++ b/packages/block-library/src/image/edit.js
@@ -2,7 +2,7 @@
* External dependencies
*/
import classnames from 'classnames';
-import { get, has, omit, pick } from 'lodash';
+import { get, has, isEmpty, omit, pick } from 'lodash';
/**
* WordPress dependencies
@@ -17,6 +17,7 @@ import {
MediaPlaceholder,
useBlockProps,
store as blockEditorStore,
+ __experimentalUseBorderProps as useBorderProps,
} from '@wordpress/block-editor';
import { useEffect, useRef, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
@@ -296,10 +297,14 @@ export function ImageEdit( {
/>
);
+ const borderProps = useBorderProps( attributes );
+
const classes = classnames( className, {
'is-transient': temporaryURL,
'is-resized': !! width || !! height,
[ `size-${ sizeSlug }` ]: sizeSlug,
+ 'has-custom-border':
+ !! borderProps.className || ! isEmpty( borderProps.style ),
} );
const blockProps = useBlockProps( {
diff --git a/packages/block-library/src/image/editor.scss b/packages/block-library/src/image/editor.scss
index bcbcb771bd1cbf..52a8e47198d362 100644
--- a/packages/block-library/src/image/editor.scss
+++ b/packages/block-library/src/image/editor.scss
@@ -22,10 +22,6 @@ figure.wp-block-image:not(.wp-block) {
margin-top: -9px;
margin-left: -9px;
}
-
- &:not(.is-style-rounded) > div:not(.components-placeholder) {
- border-radius: inherit;
- }
}
// This is necessary for the editor resize handles to accurately work on a non-floated, non-resized, small image.
@@ -94,6 +90,15 @@ figure.wp-block-image:not(.wp-block) {
position: relative;
max-width: 100%;
width: 100%;
+ overflow: hidden;
+
+ // This removes the border from the img within the image cropper so it
+ // can be applied to the cropper itself. This then allows the image to be
+ // cropped within the visual border providing more accurate editing and
+ // smoother UX.
+ .reactEasyCrop_Container .reactEasyCrop_Image {
+ border: none;
+ }
}
.wp-block-image__crop-icon {
diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js
index 52fa307cecac80..1275af2eb2248d 100644
--- a/packages/block-library/src/image/image.js
+++ b/packages/block-library/src/image/image.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { get, filter, map, pick, includes } from 'lodash';
+import { get, filter, isEmpty, map, pick, includes } from 'lodash';
/**
* WordPress dependencies
@@ -30,6 +30,7 @@ import {
__experimentalImageEditor as ImageEditor,
__experimentalImageEditingProvider as ImageEditingProvider,
__experimentalGetElementClassName,
+ __experimentalUseBorderProps as useBorderProps,
} from '@wordpress/block-editor';
import { useEffect, useMemo, useState, useRef } from '@wordpress/element';
import { __, sprintf, isRTL } from '@wordpress/i18n';
@@ -57,7 +58,19 @@ import { MIN_SIZE, ALLOWED_MEDIA_TYPES } from './constants';
export default function Image( {
temporaryURL,
- attributes: {
+ attributes,
+ setAttributes,
+ isSelected,
+ insertBlocksAfter,
+ onReplace,
+ onSelectImage,
+ onSelectURL,
+ onUploadError,
+ containerRef,
+ context,
+ clientId,
+} ) {
+ const {
url = '',
alt,
caption,
@@ -72,18 +85,7 @@ export default function Image( {
height,
linkTarget,
sizeSlug,
- },
- setAttributes,
- isSelected,
- insertBlocksAfter,
- onReplace,
- onSelectImage,
- onSelectURL,
- onUploadError,
- containerRef,
- context,
- clientId,
-} ) {
+ } = attributes;
const imageRef = useRef();
const captionRef = useRef();
const prevUrl = usePrevious( url );
@@ -186,7 +188,7 @@ export default function Image( {
// Get naturalWidth and naturalHeight from image ref, and fall back to loaded natural
// width and height. This resolves an issue in Safari where the loaded natural
- // witdth and height is otherwise lost when switching between alignments.
+ // width and height is otherwise lost when switching between alignments.
// See: https://github.com/WordPress/gutenberg/pull/37210.
const { naturalWidth, naturalHeight } = useMemo( () => {
return {
@@ -429,6 +431,11 @@ export default function Image( {
defaultedAlt = __( 'This image has an empty alt attribute' );
}
+ const borderProps = useBorderProps( attributes );
+ const isRounded = attributes.className?.includes( 'is-style-rounded' );
+ const hasCustomBorder =
+ !! borderProps.className || ! isEmpty( borderProps.style );
+
let img = (
// Disable reason: Image itself is not meant to be interactive, but
// should direct focus to block.
@@ -445,6 +452,8 @@ export default function Image( {
} );
} }
ref={ imageRef }
+ className={ borderProps.className }
+ style={ borderProps.style }
/>
{ temporaryURL && }
>
@@ -466,6 +475,7 @@ export default function Image( {
if ( canEditImage && isEditingImage ) {
img = (
a,
+ &.has-custom-border {
img {
- border-radius: inherit;
+ box-sizing: border-box;
}
}
@@ -97,6 +97,42 @@
border-radius: 0;
}
}
+
+ // The following is required to overcome WP Core applying styles that clear
+ // img borders with a higher specificity than those added by the border
+ // block support to provide a default border-style of solid when a border
+ // color or width has been set.
+ :where(.has-border-color) {
+ border-style: solid;
+ }
+ :where([style*="border-top-color"]) {
+ border-top-style: solid;
+ }
+ :where([style*="border-right-color"]) {
+ border-right-style: solid;
+ }
+ :where([style*="border-bottom-color"]) {
+ border-bottom-style: solid;
+ }
+ :where([style*="border-left-color"]) {
+ border-left-style: solid;
+ }
+
+ :where([style*="border-width"]) {
+ border-style: solid;
+ }
+ :where([style*="border-top-width"]) {
+ border-top-style: solid;
+ }
+ :where([style*="border-right-width"]) {
+ border-right-style: solid;
+ }
+ :where([style*="border-bottom-width"]) {
+ border-bottom-style: solid;
+ }
+ :where([style*="border-left-width"]) {
+ border-left-style: solid;
+ }
}
.wp-block-image figure {
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.html b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.html
index 68626f719fbcdb..8cddf1e7d7c04b 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.html
+++ b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.html
@@ -1,3 +1,3 @@
-
-
+
+
\ No newline at end of file
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.json b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.json
index c613b3716e8f0b..ae3bd53950a35b 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.json
+++ b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.json
@@ -10,13 +10,8 @@
"id": 13,
"width": 358,
"height": 164,
- "linkDestination": "none",
"sizeSlug": "large",
- "style": {
- "border": {
- "radius": "10px"
- }
- }
+ "linkDestination": "none"
},
"innerBlocks": []
}
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.parsed.json
index 82c2a2ac866b19..249f11175df421 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.parsed.json
+++ b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.parsed.json
@@ -7,17 +7,12 @@
"width": 358,
"height": 164,
"sizeSlug": "large",
- "linkDestination": "none",
- "style": {
- "border": {
- "radius": "10px"
- }
- }
+ "linkDestination": "none"
},
"innerBlocks": [],
- "innerHTML": "\n\n",
+ "innerHTML": "\n\n",
"innerContent": [
- "\n\n"
+ "\n\n"
]
}
]
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.serialized.html
index 5037f335ad989e..5ba1eb754e83f6 100644
--- a/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.serialized.html
+++ b/test/integration/fixtures/blocks/core__image__deprecated-align-wrapper.serialized.html
@@ -1,3 +1,3 @@
-
-
+
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.html b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.html
new file mode 100644
index 00000000000000..f9ca0510e1af36
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.json b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.json
new file mode 100644
index 00000000000000..093398e1d54b76
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.json
@@ -0,0 +1,20 @@
+[
+ {
+ "name": "core/image",
+ "isValid": true,
+ "attributes": {
+ "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",
+ "alt": "",
+ "caption": "",
+ "id": 246,
+ "sizeSlug": "large",
+ "linkDestination": "none",
+ "style": {
+ "border": {
+ "radius": "50px"
+ }
+ }
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.parsed.json
new file mode 100644
index 00000000000000..4122c71e044696
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.parsed.json
@@ -0,0 +1,20 @@
+[
+ {
+ "blockName": "core/image",
+ "attrs": {
+ "id": 246,
+ "sizeSlug": "large",
+ "linkDestination": "none",
+ "style": {
+ "border": {
+ "radius": "50px"
+ }
+ }
+ },
+ "innerBlocks": [],
+ "innerHTML": "\n
\n",
+ "innerContent": [
+ "\n
\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.serialized.html
new file mode 100644
index 00000000000000..c91edbc3363f37
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__image__deprecated-border-radius-5.serialized.html
@@ -0,0 +1,3 @@
+
+
+