inspector-controls
2 years ago
AlignmentControls.js
4 years ago
AnchorTag.js
4 years ago
BlockControls.js
2 years ago
ComponentCSS.js
1 year ago
Image.js
4 years ago
ImageContentRenderer.js
3 years ago
ImagePlaceholder.js
4 years ago
ImageContentRenderer.js
157 lines
| 1 | import ImagePlaceholder from './ImagePlaceholder'; |
| 2 | import { useBlockProps, store as blockEditorStore } from '@wordpress/block-editor'; |
| 3 | import classnames from 'classnames'; |
| 4 | import RootElement from '../../../components/root-element'; |
| 5 | import Element from '../../../components/element'; |
| 6 | import Image from './Image'; |
| 7 | import BlockControls from './BlockControls'; |
| 8 | import { useRef, useState, useMemo } from '@wordpress/element'; |
| 9 | import { useSelect } from '@wordpress/data'; |
| 10 | import getDynamicImage from '../../../utils/get-dynamic-image'; |
| 11 | import getMediaUrl from '../../../utils/get-media-url'; |
| 12 | import { applyFilters, doAction } from '@wordpress/hooks'; |
| 13 | |
| 14 | export default function ImageContentRenderer( props ) { |
| 15 | const { |
| 16 | attributes, |
| 17 | setAttributes, |
| 18 | name, |
| 19 | clientId, |
| 20 | deviceType, |
| 21 | temporaryURL, |
| 22 | } = props; |
| 23 | |
| 24 | const { |
| 25 | uniqueId, |
| 26 | useDynamicData, |
| 27 | dynamicContentType, |
| 28 | href, |
| 29 | openInNewWindow, |
| 30 | relNoFollow, |
| 31 | relSponsored, |
| 32 | sizeSlug, |
| 33 | className, |
| 34 | align, |
| 35 | } = attributes; |
| 36 | |
| 37 | const imageRef = useRef(); |
| 38 | const figureRef = useRef(); |
| 39 | const [ |
| 40 | { loadedNaturalWidth, loadedNaturalHeight }, |
| 41 | setLoadedNaturalSize, |
| 42 | ] = useState( {} ); |
| 43 | |
| 44 | // Get naturalWidth and naturalHeight from image ref, and fall back to loaded natural |
| 45 | // width and height. This resolves an issue in Safari where the loaded natural |
| 46 | // witdth and height is otherwise lost when switching between alignments. |
| 47 | // See: https://github.com/WordPress/gutenberg/pull/37210. |
| 48 | const { naturalWidth, naturalHeight } = useMemo( () => { |
| 49 | return { |
| 50 | naturalWidth: |
| 51 | imageRef.current?.naturalWidth || |
| 52 | loadedNaturalWidth || |
| 53 | undefined, |
| 54 | naturalHeight: |
| 55 | imageRef.current?.naturalHeight || |
| 56 | loadedNaturalHeight || |
| 57 | undefined, |
| 58 | }; |
| 59 | }, [ |
| 60 | loadedNaturalWidth, |
| 61 | loadedNaturalHeight, |
| 62 | imageRef.current?.complete, |
| 63 | ] ); |
| 64 | |
| 65 | const { |
| 66 | getBlockRootClientId, |
| 67 | } = useSelect( ( select ) => select( 'core/block-editor' ), [] ); |
| 68 | |
| 69 | const parentBlock = getBlockRootClientId( clientId ); |
| 70 | const currentImage = getDynamicImage( props ); |
| 71 | const dynamicImageUrl = getMediaUrl( currentImage, sizeSlug ); |
| 72 | |
| 73 | const dynamicImageFallback = applyFilters( |
| 74 | 'generateblocks.editor.dynamicImageFallback', |
| 75 | dynamicImageUrl, |
| 76 | props |
| 77 | ); |
| 78 | |
| 79 | const imageUrl = useDynamicData && dynamicContentType ? dynamicImageFallback : attributes.mediaUrl; |
| 80 | const altText = useDynamicData && dynamicContentType ? currentImage?.alt_text : attributes.alt; |
| 81 | const titleText = useDynamicData && dynamicContentType ? currentImage?.title?.rendered : attributes.title; |
| 82 | const supportsLayout = useSelect( ( select ) => { |
| 83 | const { |
| 84 | getSettings, |
| 85 | } = select( blockEditorStore ); |
| 86 | |
| 87 | return getSettings().supportsLayout || false; |
| 88 | }, [] ); |
| 89 | |
| 90 | const figureAttributes = useBlockProps( { |
| 91 | className: classnames( { |
| 92 | 'gb-block-image': true, |
| 93 | [ `gb-block-image-${ uniqueId }` ]: true, |
| 94 | 'is-applying': !! temporaryURL, |
| 95 | [ `align${ align }` ]: !! parentBlock && supportsLayout, |
| 96 | } ), |
| 97 | 'data-align': !! parentBlock && ! supportsLayout ? align : null, |
| 98 | ref: figureRef, |
| 99 | } ); |
| 100 | |
| 101 | // We don't want our className appearing in the figure. |
| 102 | if ( figureAttributes.className.includes( className ) ) { |
| 103 | figureAttributes.className = figureAttributes.className.replace( className, '' ).trim(); |
| 104 | } |
| 105 | |
| 106 | const canUploadImage = |
| 107 | ! useDynamicData || |
| 108 | ( |
| 109 | useDynamicData && |
| 110 | ! dynamicContentType |
| 111 | ); |
| 112 | |
| 113 | const anchorAttributes = { |
| 114 | href, |
| 115 | openInNewWindow, |
| 116 | relNoFollow, |
| 117 | relSponsored, |
| 118 | disabled: true, |
| 119 | }; |
| 120 | |
| 121 | const imageProps = { |
| 122 | src: imageUrl, |
| 123 | alt: altText, |
| 124 | title: titleText, |
| 125 | setAttributes, |
| 126 | anchorAttributes, |
| 127 | imageRef, |
| 128 | setLoadedNaturalSize, |
| 129 | naturalWidth, |
| 130 | naturalHeight, |
| 131 | attributes, |
| 132 | temporaryURL, |
| 133 | }; |
| 134 | |
| 135 | doAction( 'generateblocks.editor.renderBlock', { ...props, ref: imageRef } ); |
| 136 | |
| 137 | return ( |
| 138 | <> |
| 139 | <BlockControls |
| 140 | { ...props } |
| 141 | imageUrl={ imageUrl } |
| 142 | canUploadImage={ canUploadImage } |
| 143 | deviceType={ deviceType } |
| 144 | /> |
| 145 | |
| 146 | <RootElement name={ name } clientId={ clientId } align={ align }> |
| 147 | <Element tagName="figure" htmlAttrs={ figureAttributes }> |
| 148 | { ( !! temporaryURL || !! imageUrl ) |
| 149 | ? <Image { ...imageProps } /> |
| 150 | : <ImagePlaceholder { ...props } canUploadImage={ canUploadImage } /> |
| 151 | } |
| 152 | </Element> |
| 153 | </RootElement> |
| 154 | </> |
| 155 | ); |
| 156 | } |
| 157 |