UNPKG

3.95 kBJavaScriptView Raw
1/**
2 * External dependencies
3 */
4import classnames from 'classnames';
5
6/**
7 * WordPress dependencies
8 */
9import { Component, Fragment } from '@wordpress/element';
10import { IconButton, Spinner } from '@wordpress/components';
11import { __ } from '@wordpress/i18n';
12import { BACKSPACE, DELETE } from '@wordpress/keycodes';
13import { withSelect } from '@wordpress/data';
14import { RichText } from '@wordpress/block-editor';
15import { isBlobURL } from '@wordpress/blob';
16
17class GalleryImage extends Component {
18 constructor() {
19 super( ...arguments );
20
21 this.onSelectImage = this.onSelectImage.bind( this );
22 this.onSelectCaption = this.onSelectCaption.bind( this );
23 this.onRemoveImage = this.onRemoveImage.bind( this );
24 this.bindContainer = this.bindContainer.bind( this );
25
26 this.state = {
27 captionSelected: false,
28 };
29 }
30
31 bindContainer( ref ) {
32 this.container = ref;
33 }
34
35 onSelectCaption() {
36 if ( ! this.state.captionSelected ) {
37 this.setState( {
38 captionSelected: true,
39 } );
40 }
41
42 if ( ! this.props.isSelected ) {
43 this.props.onSelect();
44 }
45 }
46
47 onSelectImage() {
48 if ( ! this.props.isSelected ) {
49 this.props.onSelect();
50 }
51
52 if ( this.state.captionSelected ) {
53 this.setState( {
54 captionSelected: false,
55 } );
56 }
57 }
58
59 onRemoveImage( event ) {
60 if (
61 this.container === document.activeElement &&
62 this.props.isSelected && [ BACKSPACE, DELETE ].indexOf( event.keyCode ) !== -1
63 ) {
64 event.stopPropagation();
65 event.preventDefault();
66 this.props.onRemove();
67 }
68 }
69
70 componentDidUpdate( prevProps ) {
71 const { isSelected, image, url } = this.props;
72 if ( image && ! url ) {
73 this.props.setAttributes( {
74 url: image.source_url,
75 alt: image.alt_text,
76 } );
77 }
78
79 // unselect the caption so when the user selects other image and comeback
80 // the caption is not immediately selected
81 if ( this.state.captionSelected && ! isSelected && prevProps.isSelected ) {
82 this.setState( {
83 captionSelected: false,
84 } );
85 }
86 }
87
88 render() {
89 const { url, alt, id, linkTo, link, isSelected, caption, onRemove, setAttributes, 'aria-label': ariaLabel } = this.props;
90
91 let href;
92
93 switch ( linkTo ) {
94 case 'media':
95 href = url;
96 break;
97 case 'attachment':
98 href = link;
99 break;
100 }
101
102 const img = (
103 // Disable reason: Image itself is not meant to be interactive, but should
104 // direct image selection and unfocus caption fields.
105 /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
106 <Fragment>
107 <img
108 src={ url }
109 alt={ alt }
110 data-id={ id }
111 onClick={ this.onSelectImage }
112 onFocus={ this.onSelectImage }
113 onKeyDown={ this.onRemoveImage }
114 tabIndex="0"
115 aria-label={ ariaLabel }
116 ref={ this.bindContainer }
117 />
118 { isBlobURL( url ) && <Spinner /> }
119 </Fragment>
120 /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */
121 );
122
123 const className = classnames( {
124 'is-selected': isSelected,
125 'is-transient': isBlobURL( url ),
126 } );
127
128 return (
129 <figure className={ className }>
130 { isSelected &&
131 <div className="block-library-gallery-item__inline-menu">
132 <IconButton
133 icon="no-alt"
134 onClick={ onRemove }
135 className="blocks-gallery-item__remove"
136 label={ __( 'Remove Image' ) }
137 />
138 </div>
139 }
140 { href ? <a href={ href }>{ img }</a> : img }
141 { ( ! RichText.isEmpty( caption ) || isSelected ) ? (
142 <RichText
143 tagName="figcaption"
144 placeholder={ __( 'Write caption…' ) }
145 value={ caption }
146 isSelected={ this.state.captionSelected }
147 onChange={ ( newCaption ) => setAttributes( { caption: newCaption } ) }
148 unstableOnFocus={ this.onSelectCaption }
149 inlineToolbar
150 />
151 ) : null }
152 </figure>
153 );
154 }
155}
156
157export default withSelect( ( select, ownProps ) => {
158 const { getMedia } = select( 'core' );
159 const { id } = ownProps;
160
161 return {
162 image: id ? getMedia( id ) : null,
163 };
164} )( GalleryImage );