UNPKG

@wordpress/block-library

Version:
165 lines (144 loc) 3.95 kB
/** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { Component, Fragment } from '@wordpress/element'; import { IconButton, Spinner } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { BACKSPACE, DELETE } from '@wordpress/keycodes'; import { withSelect } from '@wordpress/data'; import { RichText } from '@wordpress/block-editor'; import { isBlobURL } from '@wordpress/blob'; class GalleryImage extends Component { constructor() { super( ...arguments ); this.onSelectImage = this.onSelectImage.bind( this ); this.onSelectCaption = this.onSelectCaption.bind( this ); this.onRemoveImage = this.onRemoveImage.bind( this ); this.bindContainer = this.bindContainer.bind( this ); this.state = { captionSelected: false, }; } bindContainer( ref ) { this.container = ref; } onSelectCaption() { if ( ! this.state.captionSelected ) { this.setState( { captionSelected: true, } ); } if ( ! this.props.isSelected ) { this.props.onSelect(); } } onSelectImage() { if ( ! this.props.isSelected ) { this.props.onSelect(); } if ( this.state.captionSelected ) { this.setState( { captionSelected: false, } ); } } onRemoveImage( event ) { if ( this.container === document.activeElement && this.props.isSelected && [ BACKSPACE, DELETE ].indexOf( event.keyCode ) !== -1 ) { event.stopPropagation(); event.preventDefault(); this.props.onRemove(); } } componentDidUpdate( prevProps ) { const { isSelected, image, url } = this.props; if ( image && ! url ) { this.props.setAttributes( { url: image.source_url, alt: image.alt_text, } ); } // unselect the caption so when the user selects other image and comeback // the caption is not immediately selected if ( this.state.captionSelected && ! isSelected && prevProps.isSelected ) { this.setState( { captionSelected: false, } ); } } render() { const { url, alt, id, linkTo, link, isSelected, caption, onRemove, setAttributes, 'aria-label': ariaLabel } = this.props; let href; switch ( linkTo ) { case 'media': href = url; break; case 'attachment': href = link; break; } const img = ( // Disable reason: Image itself is not meant to be interactive, but should // direct image selection and unfocus caption fields. /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ <Fragment> <img src={ url } alt={ alt } data-id={ id } onClick={ this.onSelectImage } onFocus={ this.onSelectImage } onKeyDown={ this.onRemoveImage } tabIndex="0" aria-label={ ariaLabel } ref={ this.bindContainer } /> { isBlobURL( url ) && <Spinner /> } </Fragment> /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */ ); const className = classnames( { 'is-selected': isSelected, 'is-transient': isBlobURL( url ), } ); return ( <figure className={ className }> { isSelected && <div className="block-library-gallery-item__inline-menu"> <IconButton icon="no-alt" onClick={ onRemove } className="blocks-gallery-item__remove" label={ __( 'Remove Image' ) } /> </div> } { href ? <a href={ href }>{ img }</a> : img } { ( ! RichText.isEmpty( caption ) || isSelected ) ? ( <RichText tagName="figcaption" placeholder={ __( 'Write caption…' ) } value={ caption } isSelected={ this.state.captionSelected } onChange={ ( newCaption ) => setAttributes( { caption: newCaption } ) } unstableOnFocus={ this.onSelectCaption } inlineToolbar /> ) : null } </figure> ); } } export default withSelect( ( select, ownProps ) => { const { getMedia } = select( 'core' ); const { id } = ownProps; return { image: id ? getMedia( id ) : null, }; } )( GalleryImage );