import ImagePlaceholder from './ImagePlaceholder';
import { useBlockProps, store as blockEditorStore } from '@wordpress/block-editor';
import classnames from 'classnames';
import RootElement from '../../../components/root-element';
import Element from '../../../components/element';
import Image from './Image';
import BlockControls from './BlockControls';
import { useRef, useState, useMemo } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import getDynamicImage from '../../../utils/get-dynamic-image';
import getMediaUrl from '../../../utils/get-media-url';
import { applyFilters, doAction } from '@wordpress/hooks';
export default function ImageContentRenderer( props ) {
const {
attributes,
setAttributes,
name,
clientId,
deviceType,
temporaryURL,
} = props;
const {
uniqueId,
useDynamicData,
dynamicContentType,
href,
openInNewWindow,
relNoFollow,
relSponsored,
sizeSlug,
className,
align,
} = attributes;
const imageRef = useRef();
const figureRef = useRef();
const [
{ loadedNaturalWidth, loadedNaturalHeight },
setLoadedNaturalSize,
] = useState( {} );
// 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.
// See: https://github.com/WordPress/gutenberg/pull/37210.
const { naturalWidth, naturalHeight } = useMemo( () => {
return {
naturalWidth:
imageRef.current?.naturalWidth ||
loadedNaturalWidth ||
undefined,
naturalHeight:
imageRef.current?.naturalHeight ||
loadedNaturalHeight ||
undefined,
};
}, [
loadedNaturalWidth,
loadedNaturalHeight,
imageRef.current?.complete,
] );
const {
getBlockRootClientId,
} = useSelect( ( select ) => select( 'core/block-editor' ), [] );
const parentBlock = getBlockRootClientId( clientId );
const currentImage = getDynamicImage( props );
const dynamicImageUrl = getMediaUrl( currentImage, sizeSlug );
const dynamicImageFallback = applyFilters(
'generateblocks.editor.dynamicImageFallback',
dynamicImageUrl,
props
);
const imageUrl = useDynamicData && dynamicContentType ? dynamicImageFallback : attributes.mediaUrl;
const altText = useDynamicData && dynamicContentType ? currentImage?.alt_text : attributes.alt;
const titleText = useDynamicData && dynamicContentType ? currentImage?.title?.rendered : attributes.title;
const supportsLayout = useSelect( ( select ) => {
const {
getSettings,
} = select( blockEditorStore );
return getSettings().supportsLayout || false;
}, [] );
const figureAttributes = useBlockProps( {
className: classnames( {
'gb-block-image': true,
[ `gb-block-image-${ uniqueId }` ]: true,
'is-applying': !! temporaryURL,
[ `align${ align }` ]: !! parentBlock && supportsLayout,
} ),
'data-align': !! parentBlock && ! supportsLayout ? align : null,
ref: figureRef,
} );
// We don't want our className appearing in the figure.
if ( figureAttributes.className.includes( className ) ) {
figureAttributes.className = figureAttributes.className.replace( className, '' ).trim();
}
const canUploadImage =
! useDynamicData ||
(
useDynamicData &&
! dynamicContentType
);
const anchorAttributes = {
href,
openInNewWindow,
relNoFollow,
relSponsored,
disabled: true,
};
const imageProps = {
src: imageUrl,
alt: altText,
title: titleText,
setAttributes,
anchorAttributes,
imageRef,
setLoadedNaturalSize,
naturalWidth,
naturalHeight,
attributes,
temporaryURL,
};
doAction( 'generateblocks.editor.renderBlock', { ...props, ref: imageRef } );
return (
<>
<BlockControls
{ ...props }
imageUrl={ imageUrl }
canUploadImage={ canUploadImage }
deviceType={ deviceType }
/>
<RootElement name={ name } clientId={ clientId } align={ align }>
<Element tagName="figure" htmlAttrs={ figureAttributes }>
{ ( !! temporaryURL || !! imageUrl )
? <Image { ...imageProps } />
: <ImagePlaceholder { ...props } canUploadImage={ canUploadImage } />
}
</Element>
</RootElement>
</>
);
}