diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 2917c8577b07d..7d8ff54432c79 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -555,7 +555,7 @@ Display a post's featured image. ([Source](https://github.com/WordPress/gutenber - **Name:** core/post-featured-image - **Category:** theme - **Supports:** align (center, full, left, right, wide), anchor, color (~~background~~, ~~text~~), spacing (margin, padding), ~~html~~ -- **Attributes:** aspectRatio, customGradient, customOverlayColor, dimRatio, gradient, height, isLink, linkTarget, overlayColor, rel, scale, sizeSlug, width +- **Attributes:** aspectRatio, caption, customGradient, customOverlayColor, dimRatio, displayCaption, gradient, height, isLink, linkTarget, overlayColor, rel, scale, sizeSlug, width ## Post Navigation Link diff --git a/packages/block-library/src/post-featured-image/block.json b/packages/block-library/src/post-featured-image/block.json index c6007785cd82a..f971bc7ded91d 100644 --- a/packages/block-library/src/post-featured-image/block.json +++ b/packages/block-library/src/post-featured-image/block.json @@ -51,6 +51,13 @@ }, "customGradient": { "type": "string" + }, + "displayCaption": { + "type": "boolean", + "default": false + }, + "caption": { + "type": "string" } }, "usesContext": [ "postId", "postType", "queryId" ], diff --git a/packages/block-library/src/post-featured-image/edit.js b/packages/block-library/src/post-featured-image/edit.js index f378a14f5c9d1..d88f8756e2d41 100644 --- a/packages/block-library/src/post-featured-image/edit.js +++ b/packages/block-library/src/post-featured-image/edit.js @@ -15,6 +15,7 @@ import { Placeholder, Button, TextControl, + ToolbarButton, } from '@wordpress/components'; import { InspectorControls, @@ -23,11 +24,16 @@ import { MediaReplaceFlow, useBlockProps, store as blockEditorStore, + __experimentalGetElementClassName, __experimentalUseBorderProps as useBorderProps, + RichText, } from '@wordpress/block-editor'; import { __, sprintf } from '@wordpress/i18n'; -import { upload } from '@wordpress/icons'; +import { createBlock, getDefaultBlockName } from '@wordpress/blocks'; +import { upload, caption as captionIcon } from '@wordpress/icons'; import { store as noticesStore } from '@wordpress/notices'; +import { useEffect, useState, useCallback } from '@wordpress/element'; +import { usePrevious } from '@wordpress/compose'; /** * Internal dependencies @@ -47,6 +53,8 @@ export default function PostFeaturedImageEdit( { clientId, attributes, setAttributes, + isSelected, + insertBlocksAfter, context: { postId, postType: postTypeSlug, queryId }, } ) { const isDescendentOfQueryLoop = Number.isFinite( queryId ); @@ -59,6 +67,7 @@ export default function PostFeaturedImageEdit( { sizeSlug, rel, linkTarget, + caption, } = attributes; const [ featuredImage, setFeaturedImage ] = useEntityProp( 'postType', @@ -67,6 +76,32 @@ export default function PostFeaturedImageEdit( { postId ); + const prevCaption = usePrevious( caption ); + const [ showCaption, setShowCaption ] = useState( true ); + // We need to show the caption when changes come from + // history navigation(undo/redo). + useEffect( () => { + if ( caption && ! prevCaption ) { + setShowCaption( true ); + } + }, [ caption, prevCaption ] ); + + // Focus the caption when we click to add one. + const captionRef = useCallback( + ( node ) => { + if ( node && ! caption ) { + node.focus(); + } + }, + [ caption ] + ); + + useEffect( () => { + if ( ! isSelected && ! caption ) { + setShowCaption( false ); + } + }, [ isSelected, caption ] ); + const { media, postType } = useSelect( ( select ) => { const { getMedia, getPostType } = select( coreStore ); @@ -83,6 +118,10 @@ export default function PostFeaturedImageEdit( { ); const mediaUrl = getMediaSourceUrlBySizeSlug( media, sizeSlug ); + const mediaLibraryCaption = !! media?.caption?.rendered + ? media?.caption?.rendered + : ''; + const imageSizes = useSelect( ( select ) => select( blockEditorStore ).getSettings().imageSizes, [] @@ -130,8 +169,35 @@ export default function PostFeaturedImageEdit( { createErrorNotice( message, { type: 'snackbar' } ); }; + // Displays the caption when it is not editable. + const DisplayFigCaption = () => ( +
+ ); + const controls = ( <> + + { + setShowCaption( ! showCaption ); + if ( showCaption && caption ) { + setAttributes( { caption: undefined } ); + } + } } + icon={ captionIcon } + isPressed={ showCaption } + label={ + showCaption + ? __( 'Remove caption' ) + : __( 'Add caption' ) + } + /> + { controls } -
+
{ placeholder() } -
+ + { showCaption && } ); } @@ -214,14 +281,15 @@ export default function PostFeaturedImageEdit( { setAttributes={ setAttributes } imageSizeOptions={ imageSizeOptions } /> -
+
{ placeholder() } -
+ { showCaption && } + ); } @@ -293,6 +361,7 @@ export default function PostFeaturedImageEdit( { * - Is not inside a query loop * Then display the image and the image replacement option. */ + return ( <> { controls } @@ -319,6 +388,32 @@ export default function PostFeaturedImageEdit( { setAttributes={ setAttributes } clientId={ clientId } /> + { showCaption && + ! isDescendentOfQueryLoop && + ( ! RichText.isEmpty( caption ) || isSelected ) && ( + + setAttributes( { caption: value } ) + } + inlineToolbar + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( + createBlock( getDefaultBlockName() ) + ) + } + /> + ) } + { showCaption && isDescendentOfQueryLoop && ( + + ) } ); diff --git a/packages/block-library/src/post-featured-image/index.php b/packages/block-library/src/post-featured-image/index.php index 6cb4110ee000e..0d4f1d8c33675 100644 --- a/packages/block-library/src/post-featured-image/index.php +++ b/packages/block-library/src/post-featured-image/index.php @@ -25,10 +25,12 @@ function render_block_core_post_featured_image( $attributes, $content, $block ) the_post(); } - $is_link = isset( $attributes['isLink'] ) && $attributes['isLink']; - $size_slug = isset( $attributes['sizeSlug'] ) ? $attributes['sizeSlug'] : 'post-thumbnail'; - $attr = get_block_core_post_featured_image_border_attributes( $attributes ); - $overlay_markup = get_block_core_post_featured_image_overlay_element_markup( $attributes ); + $is_link = isset( $attributes['isLink'] ) && $attributes['isLink']; + $size_slug = isset( $attributes['sizeSlug'] ) ? $attributes['sizeSlug'] : 'post-thumbnail'; + $attr = get_block_core_post_featured_image_border_attributes( $attributes ); + $overlay_markup = get_block_core_post_featured_image_overlay_element_markup( $attributes ); + $caption = isset( $attributes['caption'] ) ? $attributes['caption'] : ''; + $display_caption = isset( $attributes['displayCaption'] ) ? $attributes['displayCaption'] : ''; if ( $is_link ) { if ( get_the_title( $post_ID ) ) { @@ -80,6 +82,13 @@ function render_block_core_post_featured_image( $attributes, $content, $block ) $featured_image = $featured_image . $overlay_markup; } + $image_caption = ''; + if ( $caption ) { + $image_caption = '
' . $caption . '
'; + } elseif ( $display_caption ) { + $image_caption = '
' . get_the_post_thumbnail_caption() . '
'; + } + $aspect_ratio = ! empty( $attributes['aspectRatio'] ) ? esc_attr( safecss_filter_attr( 'aspect-ratio:' . $attributes['aspectRatio'] ) ) . ';' : ''; @@ -94,7 +103,7 @@ function render_block_core_post_featured_image( $attributes, $content, $block ) } else { $wrapper_attributes = get_block_wrapper_attributes( array( 'style' => $aspect_ratio . $width . $height ) ); } - return "
{$featured_image}
"; + return "
{$featured_image}{$image_caption}
"; } /** diff --git a/test/integration/fixtures/blocks/core__post-featured-image.json b/test/integration/fixtures/blocks/core__post-featured-image.json index 158007533a3f2..9acaf77f4c260 100644 --- a/test/integration/fixtures/blocks/core__post-featured-image.json +++ b/test/integration/fixtures/blocks/core__post-featured-image.json @@ -7,7 +7,8 @@ "scale": "cover", "rel": "", "linkTarget": "_self", - "dimRatio": 0 + "dimRatio": 0, + "displayCaption": false }, "innerBlocks": [] }