1 |
|
2 |
|
3 |
|
4 | import classnames from 'classnames';
|
5 | import {
|
6 | compact,
|
7 | get,
|
8 | isEmpty,
|
9 | map,
|
10 | last,
|
11 | pick,
|
12 | } from 'lodash';
|
13 |
|
14 | /**
|
15 | * WordPress dependencies
|
16 | */
|
17 | import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob';
|
18 | import {
|
19 | Button,
|
20 | ButtonGroup,
|
21 | IconButton,
|
22 | PanelBody,
|
23 | ResizableBox,
|
24 | SelectControl,
|
25 | Spinner,
|
26 | TextareaControl,
|
27 | TextControl,
|
28 | ToggleControl,
|
29 | Toolbar,
|
30 | withNotices,
|
31 | } from '@wordpress/components';
|
32 | import { compose } from '@wordpress/compose';
|
33 | import { withSelect } from '@wordpress/data';
|
34 | import {
|
35 | BlockAlignmentToolbar,
|
36 | BlockControls,
|
37 | BlockIcon,
|
38 | InspectorControls,
|
39 | MediaPlaceholder,
|
40 | MediaUpload,
|
41 | MediaUploadCheck,
|
42 | RichText,
|
43 | } from '@wordpress/block-editor';
|
44 | import { mediaUpload } from '@wordpress/editor';
|
45 | import { Component, Fragment } from '@wordpress/element';
|
46 | import { __, sprintf } from '@wordpress/i18n';
|
47 | import { getPath } from '@wordpress/url';
|
48 | import { withViewportMatch } from '@wordpress/viewport';
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | import { createUpgradedEmbedBlock } from '../embed/util';
|
54 | import icon from './icon';
|
55 | import ImageSize from './image-size';
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | const MIN_SIZE = 20;
|
61 | const LINK_DESTINATION_NONE = 'none';
|
62 | const LINK_DESTINATION_MEDIA = 'media';
|
63 | const LINK_DESTINATION_ATTACHMENT = 'attachment';
|
64 | const LINK_DESTINATION_CUSTOM = 'custom';
|
65 | const NEW_TAB_REL = 'noreferrer noopener';
|
66 | const ALLOWED_MEDIA_TYPES = [ 'image' ];
|
67 |
|
68 | export const pickRelevantMediaFiles = ( image ) => {
|
69 | const imageProps = pick( image, [ 'alt', 'id', 'link', 'caption' ] );
|
70 | imageProps.url = get( image, [ 'sizes', 'large', 'url' ] ) || get( image, [ 'media_details', 'sizes', 'large', 'source_url' ] ) || image.url;
|
71 | return imageProps;
|
72 | };
|
73 |
|
74 | /**
|
75 | * Is the URL a temporary blob URL? A blob URL is one that is used temporarily
|
76 | * while the image is being uploaded and will not have an id yet allocated.
|
77 | *
|
78 | * @param {number=} id The id of the image.
|
79 | * @param {string=} url The url of the image.
|
80 | *
|
81 | * @return {boolean} Is the URL a Blob URL
|
82 | */
|
83 | const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url );
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | const isExternalImage = ( id, url ) => url && ! id && ! isBlobURL( url );
|
95 |
|
96 | class ImageEdit extends Component {
|
97 | constructor( { attributes } ) {
|
98 | super( ...arguments );
|
99 | this.updateAlt = this.updateAlt.bind( this );
|
100 | this.updateAlignment = this.updateAlignment.bind( this );
|
101 | this.onFocusCaption = this.onFocusCaption.bind( this );
|
102 | this.onImageClick = this.onImageClick.bind( this );
|
103 | this.onSelectImage = this.onSelectImage.bind( this );
|
104 | this.onSelectURL = this.onSelectURL.bind( this );
|
105 | this.updateImageURL = this.updateImageURL.bind( this );
|
106 | this.updateWidth = this.updateWidth.bind( this );
|
107 | this.updateHeight = this.updateHeight.bind( this );
|
108 | this.updateDimensions = this.updateDimensions.bind( this );
|
109 | this.onSetCustomHref = this.onSetCustomHref.bind( this );
|
110 | this.onSetLinkClass = this.onSetLinkClass.bind( this );
|
111 | this.onSetLinkRel = this.onSetLinkRel.bind( this );
|
112 | this.onSetLinkDestination = this.onSetLinkDestination.bind( this );
|
113 | this.onSetNewTab = this.onSetNewTab.bind( this );
|
114 | this.getFilename = this.getFilename.bind( this );
|
115 | this.toggleIsEditing = this.toggleIsEditing.bind( this );
|
116 | this.onUploadError = this.onUploadError.bind( this );
|
117 | this.onImageError = this.onImageError.bind( this );
|
118 |
|
119 | this.state = {
|
120 | captionFocused: false,
|
121 | isEditing: ! attributes.url,
|
122 | };
|
123 | }
|
124 |
|
125 | componentDidMount() {
|
126 | const { attributes, setAttributes, noticeOperations } = this.props;
|
127 | const { id, url = '' } = attributes;
|
128 |
|
129 | if ( isTemporaryImage( id, url ) ) {
|
130 | const file = getBlobByURL( url );
|
131 |
|
132 | if ( file ) {
|
133 | mediaUpload( {
|
134 | filesList: [ file ],
|
135 | onFileChange: ( [ image ] ) => {
|
136 | setAttributes( pickRelevantMediaFiles( image ) );
|
137 | },
|
138 | allowedTypes: ALLOWED_MEDIA_TYPES,
|
139 | onError: ( message ) => {
|
140 | noticeOperations.createErrorNotice( message );
|
141 | this.setState( { isEditing: true } );
|
142 | },
|
143 | } );
|
144 | }
|
145 | }
|
146 | }
|
147 |
|
148 | componentDidUpdate( prevProps ) {
|
149 | const { id: prevID, url: prevURL = '' } = prevProps.attributes;
|
150 | const { id, url = '' } = this.props.attributes;
|
151 |
|
152 | if ( isTemporaryImage( prevID, prevURL ) && ! isTemporaryImage( id, url ) ) {
|
153 | revokeBlobURL( url );
|
154 | }
|
155 |
|
156 | if ( ! this.props.isSelected && prevProps.isSelected && this.state.captionFocused ) {
|
157 | this.setState( {
|
158 | captionFocused: false,
|
159 | } );
|
160 | }
|
161 | }
|
162 |
|
163 | onUploadError( message ) {
|
164 | const { noticeOperations } = this.props;
|
165 | noticeOperations.createErrorNotice( message );
|
166 | this.setState( {
|
167 | isEditing: true,
|
168 | } );
|
169 | }
|
170 |
|
171 | onSelectImage( media ) {
|
172 | if ( ! media || ! media.url ) {
|
173 | this.props.setAttributes( {
|
174 | url: undefined,
|
175 | alt: undefined,
|
176 | id: undefined,
|
177 | caption: undefined,
|
178 | } );
|
179 | return;
|
180 | }
|
181 |
|
182 | this.setState( {
|
183 | isEditing: false,
|
184 | } );
|
185 |
|
186 | this.props.setAttributes( {
|
187 | ...pickRelevantMediaFiles( media ),
|
188 | width: undefined,
|
189 | height: undefined,
|
190 | } );
|
191 | }
|
192 |
|
193 | onSetLinkDestination( value ) {
|
194 | let href;
|
195 |
|
196 | if ( value === LINK_DESTINATION_NONE ) {
|
197 | href = undefined;
|
198 | } else if ( value === LINK_DESTINATION_MEDIA ) {
|
199 | href = ( this.props.image && this.props.image.source_url ) || this.props.attributes.url;
|
200 | } else if ( value === LINK_DESTINATION_ATTACHMENT ) {
|
201 | href = this.props.image && this.props.image.link;
|
202 | } else {
|
203 | href = this.props.attributes.href;
|
204 | }
|
205 |
|
206 | this.props.setAttributes( {
|
207 | linkDestination: value,
|
208 | href,
|
209 | } );
|
210 | }
|
211 |
|
212 | onSelectURL( newURL ) {
|
213 | const { url } = this.props.attributes;
|
214 |
|
215 | if ( newURL !== url ) {
|
216 | this.props.setAttributes( {
|
217 | url: newURL,
|
218 | id: undefined,
|
219 | } );
|
220 | }
|
221 |
|
222 | this.setState( {
|
223 | isEditing: false,
|
224 | } );
|
225 | }
|
226 |
|
227 | onImageError( url ) {
|
228 |
|
229 | const embedBlock = createUpgradedEmbedBlock(
|
230 | { attributes: { url } }
|
231 | );
|
232 | if ( undefined !== embedBlock ) {
|
233 | this.props.onReplace( embedBlock );
|
234 | }
|
235 | }
|
236 |
|
237 | onSetCustomHref( value ) {
|
238 | this.props.setAttributes( { href: value } );
|
239 | }
|
240 |
|
241 | onSetLinkClass( value ) {
|
242 | this.props.setAttributes( { linkClass: value } );
|
243 | }
|
244 |
|
245 | onSetLinkRel( value ) {
|
246 | this.props.setAttributes( { rel: value } );
|
247 | }
|
248 |
|
249 | onSetNewTab( value ) {
|
250 | const { rel } = this.props.attributes;
|
251 | const linkTarget = value ? '_blank' : undefined;
|
252 |
|
253 | let updatedRel = rel;
|
254 | if ( linkTarget && ! rel ) {
|
255 | updatedRel = NEW_TAB_REL;
|
256 | } else if ( ! linkTarget && rel === NEW_TAB_REL ) {
|
257 | updatedRel = undefined;
|
258 | }
|
259 |
|
260 | this.props.setAttributes( {
|
261 | linkTarget,
|
262 | rel: updatedRel,
|
263 | } );
|
264 | }
|
265 |
|
266 | onFocusCaption() {
|
267 | if ( ! this.state.captionFocused ) {
|
268 | this.setState( {
|
269 | captionFocused: true,
|
270 | } );
|
271 | }
|
272 | }
|
273 |
|
274 | onImageClick() {
|
275 | if ( this.state.captionFocused ) {
|
276 | this.setState( {
|
277 | captionFocused: false,
|
278 | } );
|
279 | }
|
280 | }
|
281 |
|
282 | updateAlt( newAlt ) {
|
283 | this.props.setAttributes( { alt: newAlt } );
|
284 | }
|
285 |
|
286 | updateAlignment( nextAlign ) {
|
287 | const extraUpdatedAttributes = [ 'wide', 'full' ].indexOf( nextAlign ) !== -1 ?
|
288 | { width: undefined, height: undefined } :
|
289 | {};
|
290 | this.props.setAttributes( { ...extraUpdatedAttributes, align: nextAlign } );
|
291 | }
|
292 |
|
293 | updateImageURL( url ) {
|
294 | this.props.setAttributes( { url, width: undefined, height: undefined } );
|
295 | }
|
296 |
|
297 | updateWidth( width ) {
|
298 | this.props.setAttributes( { width: parseInt( width, 10 ) } );
|
299 | }
|
300 |
|
301 | updateHeight( height ) {
|
302 | this.props.setAttributes( { height: parseInt( height, 10 ) } );
|
303 | }
|
304 |
|
305 | updateDimensions( width = undefined, height = undefined ) {
|
306 | return () => {
|
307 | this.props.setAttributes( { width, height } );
|
308 | };
|
309 | }
|
310 |
|
311 | getFilename( url ) {
|
312 | const path = getPath( url );
|
313 | if ( path ) {
|
314 | return last( path.split( '/' ) );
|
315 | }
|
316 | }
|
317 |
|
318 | getLinkDestinationOptions() {
|
319 | return [
|
320 | { value: LINK_DESTINATION_NONE, label: __( 'None' ) },
|
321 | { value: LINK_DESTINATION_MEDIA, label: __( 'Media File' ) },
|
322 | { value: LINK_DESTINATION_ATTACHMENT, label: __( 'Attachment Page' ) },
|
323 | { value: LINK_DESTINATION_CUSTOM, label: __( 'Custom URL' ) },
|
324 | ];
|
325 | }
|
326 |
|
327 | toggleIsEditing() {
|
328 | this.setState( {
|
329 | isEditing: ! this.state.isEditing,
|
330 | } );
|
331 | }
|
332 |
|
333 | getImageSizeOptions() {
|
334 | const { imageSizes, image } = this.props;
|
335 | return compact( map( imageSizes, ( { name, slug } ) => {
|
336 | const sizeUrl = get( image, [ 'media_details', 'sizes', slug, 'source_url' ] );
|
337 | if ( ! sizeUrl ) {
|
338 | return null;
|
339 | }
|
340 | return {
|
341 | value: sizeUrl,
|
342 | label: name,
|
343 | };
|
344 | } ) );
|
345 | }
|
346 |
|
347 | render() {
|
348 | const { isEditing } = this.state;
|
349 | const {
|
350 | attributes,
|
351 | setAttributes,
|
352 | isLargeViewport,
|
353 | isSelected,
|
354 | className,
|
355 | maxWidth,
|
356 | noticeUI,
|
357 | toggleSelection,
|
358 | isRTL,
|
359 | } = this.props;
|
360 | const {
|
361 | url,
|
362 | alt,
|
363 | caption,
|
364 | align,
|
365 | id,
|
366 | href,
|
367 | rel,
|
368 | linkClass,
|
369 | linkDestination,
|
370 | width,
|
371 | height,
|
372 | linkTarget,
|
373 | } = attributes;
|
374 | const isExternal = isExternalImage( id, url );
|
375 |
|
376 | let toolbarEditButton;
|
377 | if ( url ) {
|
378 | if ( isExternal ) {
|
379 | toolbarEditButton = (
|
380 | <Toolbar>
|
381 | <IconButton
|
382 | className="components-icon-button components-toolbar__control"
|
383 | label={ __( 'Edit image' ) }
|
384 | onClick={ this.toggleIsEditing }
|
385 | icon="edit"
|
386 | />
|
387 | </Toolbar>
|
388 | );
|
389 | } else {
|
390 | toolbarEditButton = (
|
391 | <MediaUploadCheck>
|
392 | <Toolbar>
|
393 | <MediaUpload
|
394 | onSelect={ this.onSelectImage }
|
395 | allowedTypes={ ALLOWED_MEDIA_TYPES }
|
396 | value={ id }
|
397 | render={ ( { open } ) => (
|
398 | <IconButton
|
399 | className="components-toolbar__control"
|
400 | label={ __( 'Edit image' ) }
|
401 | icon="edit"
|
402 | onClick={ open }
|
403 | />
|
404 | ) }
|
405 | />
|
406 | </Toolbar>
|
407 | </MediaUploadCheck>
|
408 | );
|
409 | }
|
410 | }
|
411 |
|
412 | const controls = (
|
413 | <BlockControls>
|
414 | <BlockAlignmentToolbar
|
415 | value={ align }
|
416 | onChange={ this.updateAlignment }
|
417 | />
|
418 | { toolbarEditButton }
|
419 | </BlockControls>
|
420 | );
|
421 |
|
422 | if ( isEditing || ! url ) {
|
423 | const src = isExternal ? url : undefined;
|
424 | return (
|
425 | <Fragment>
|
426 | { controls }
|
427 | <MediaPlaceholder
|
428 | icon={ <BlockIcon icon={ icon } /> }
|
429 | className={ className }
|
430 | onSelect={ this.onSelectImage }
|
431 | onSelectURL={ this.onSelectURL }
|
432 | notices={ noticeUI }
|
433 | onError={ this.onUploadError }
|
434 | accept="image/*"
|
435 | allowedTypes={ ALLOWED_MEDIA_TYPES }
|
436 | value={ { id, src } }
|
437 | />
|
438 | </Fragment>
|
439 | );
|
440 | }
|
441 |
|
442 | const classes = classnames( className, {
|
443 | 'is-transient': isBlobURL( url ),
|
444 | 'is-resized': !! width || !! height,
|
445 | 'is-focused': isSelected,
|
446 | } );
|
447 |
|
448 | const isResizable = [ 'wide', 'full' ].indexOf( align ) === -1 && isLargeViewport;
|
449 | const isLinkURLInputReadOnly = linkDestination !== LINK_DESTINATION_CUSTOM;
|
450 | const imageSizeOptions = this.getImageSizeOptions();
|
451 |
|
452 | const getInspectorControls = ( imageWidth, imageHeight ) => (
|
453 | <InspectorControls>
|
454 | <PanelBody title={ __( 'Image Settings' ) }>
|
455 | <TextareaControl
|
456 | label={ __( 'Alt Text (Alternative Text)' ) }
|
457 | value={ alt }
|
458 | onChange={ this.updateAlt }
|
459 | help={ __( 'Alternative text describes your image to people who can’t see it. Add a short description with its key details.' ) }
|
460 | />
|
461 | { ! isEmpty( imageSizeOptions ) && (
|
462 | <SelectControl
|
463 | label={ __( 'Image Size' ) }
|
464 | value={ url }
|
465 | options={ imageSizeOptions }
|
466 | onChange={ this.updateImageURL }
|
467 | />
|
468 | ) }
|
469 | { isResizable && (
|
470 | <div className="block-library-image__dimensions">
|
471 | <p className="block-library-image__dimensions__row">
|
472 | { __( 'Image Dimensions' ) }
|
473 | </p>
|
474 | <div className="block-library-image__dimensions__row">
|
475 | <TextControl
|
476 | type="number"
|
477 | className="block-library-image__dimensions__width"
|
478 | label={ __( 'Width' ) }
|
479 | value={ width !== undefined ? width : imageWidth }
|
480 | min={ 1 }
|
481 | onChange={ this.updateWidth }
|
482 | />
|
483 | <TextControl
|
484 | type="number"
|
485 | className="block-library-image__dimensions__height"
|
486 | label={ __( 'Height' ) }
|
487 | value={ height !== undefined ? height : imageHeight }
|
488 | min={ 1 }
|
489 | onChange={ this.updateHeight }
|
490 | />
|
491 | </div>
|
492 | <div className="block-library-image__dimensions__row">
|
493 | <ButtonGroup aria-label={ __( 'Image Size' ) }>
|
494 | { [ 25, 50, 75, 100 ].map( ( scale ) => {
|
495 | const scaledWidth = Math.round( imageWidth * ( scale / 100 ) );
|
496 | const scaledHeight = Math.round( imageHeight * ( scale / 100 ) );
|
497 |
|
498 | const isCurrent = width === scaledWidth && height === scaledHeight;
|
499 |
|
500 | return (
|
501 | <Button
|
502 | key={ scale }
|
503 | isSmall
|
504 | isPrimary={ isCurrent }
|
505 | aria-pressed={ isCurrent }
|
506 | onClick={ this.updateDimensions( scaledWidth, scaledHeight ) }
|
507 | >
|
508 | { scale }%
|
509 | </Button>
|
510 | );
|
511 | } ) }
|
512 | </ButtonGroup>
|
513 | <Button
|
514 | isSmall
|
515 | onClick={ this.updateDimensions() }
|
516 | >
|
517 | { __( 'Reset' ) }
|
518 | </Button>
|
519 | </div>
|
520 | </div>
|
521 | ) }
|
522 | </PanelBody>
|
523 | <PanelBody title={ __( 'Link Settings' ) }>
|
524 | <SelectControl
|
525 | label={ __( 'Link To' ) }
|
526 | value={ linkDestination }
|
527 | options={ this.getLinkDestinationOptions() }
|
528 | onChange={ this.onSetLinkDestination }
|
529 | />
|
530 | { linkDestination !== LINK_DESTINATION_NONE && (
|
531 | <Fragment>
|
532 | <TextControl
|
533 | label={ __( 'Link URL' ) }
|
534 | value={ href || '' }
|
535 | onChange={ this.onSetCustomHref }
|
536 | placeholder={ ! isLinkURLInputReadOnly ? 'https://' : undefined }
|
537 | readOnly={ isLinkURLInputReadOnly }
|
538 | />
|
539 | <ToggleControl
|
540 | label={ __( 'Open in New Tab' ) }
|
541 | onChange={ this.onSetNewTab }
|
542 | checked={ linkTarget === '_blank' } />
|
543 | <TextControl
|
544 | label={ __( 'Link CSS Class' ) }
|
545 | value={ linkClass || '' }
|
546 | onChange={ this.onSetLinkClass }
|
547 | />
|
548 | <TextControl
|
549 | label={ __( 'Link Rel' ) }
|
550 | value={ rel || '' }
|
551 | onChange={ this.onSetLinkRel }
|
552 | />
|
553 | </Fragment>
|
554 | ) }
|
555 | </PanelBody>
|
556 | </InspectorControls>
|
557 | );
|
558 |
|
559 |
|
560 |
|
561 | return (
|
562 | <Fragment>
|
563 | { controls }
|
564 | <figure className={ classes }>
|
565 | <ImageSize src={ url } dirtynessTrigger={ align }>
|
566 | { ( sizes ) => {
|
567 | const {
|
568 | imageWidthWithinContainer,
|
569 | imageHeightWithinContainer,
|
570 | imageWidth,
|
571 | imageHeight,
|
572 | } = sizes;
|
573 |
|
574 | const filename = this.getFilename( url );
|
575 | let defaultedAlt;
|
576 | if ( alt ) {
|
577 | defaultedAlt = alt;
|
578 | } else if ( filename ) {
|
579 | defaultedAlt = sprintf( __( 'This image has an empty alt attribute; its file name is %s' ), filename );
|
580 | } else {
|
581 | defaultedAlt = __( 'This image has an empty alt attribute' );
|
582 | }
|
583 |
|
584 | const img = (
|
585 |
|
586 |
|
587 |
|
588 | <Fragment>
|
589 | <img src={ url } alt={ defaultedAlt } onClick={ this.onImageClick } onError={ () => this.onImageError( url ) } />
|
590 | { isBlobURL( url ) && <Spinner /> }
|
591 | </Fragment>
|
592 |
|
593 | );
|
594 |
|
595 | if ( ! isResizable || ! imageWidthWithinContainer ) {
|
596 | return (
|
597 | <Fragment>
|
598 | { getInspectorControls( imageWidth, imageHeight ) }
|
599 | <div style={ { width, height } }>
|
600 | { img }
|
601 | </div>
|
602 | </Fragment>
|
603 | );
|
604 | }
|
605 |
|
606 | const currentWidth = width || imageWidthWithinContainer;
|
607 | const currentHeight = height || imageHeightWithinContainer;
|
608 |
|
609 | const ratio = imageWidth / imageHeight;
|
610 | const minWidth = imageWidth < imageHeight ? MIN_SIZE : MIN_SIZE * ratio;
|
611 | const minHeight = imageHeight < imageWidth ? MIN_SIZE : MIN_SIZE / ratio;
|
612 |
|
613 |
|
614 |
|
615 |
|
616 |
|
617 |
|
618 | const maxWidthBuffer = maxWidth * 2.5;
|
619 |
|
620 | let showRightHandle = false;
|
621 | let showLeftHandle = false;
|
622 |
|
623 |
|
624 |
|
625 | if ( align === 'center' ) {
|
626 |
|
627 | showRightHandle = true;
|
628 | showLeftHandle = true;
|
629 | } else if ( isRTL ) {
|
630 |
|
631 |
|
632 |
|
633 | if ( align === 'left' ) {
|
634 | showRightHandle = true;
|
635 | } else {
|
636 | showLeftHandle = true;
|
637 | }
|
638 | } else {
|
639 |
|
640 |
|
641 | if ( align === 'right' ) {
|
642 | showLeftHandle = true;
|
643 | } else {
|
644 | showRightHandle = true;
|
645 | }
|
646 | }
|
647 |
|
648 |
|
649 | return (
|
650 | <Fragment>
|
651 | { getInspectorControls( imageWidth, imageHeight ) }
|
652 | <ResizableBox
|
653 | size={
|
654 | width && height ? {
|
655 | width,
|
656 | height,
|
657 | } : undefined
|
658 | }
|
659 | minWidth={ minWidth }
|
660 | maxWidth={ maxWidthBuffer }
|
661 | minHeight={ minHeight }
|
662 | maxHeight={ maxWidthBuffer / ratio }
|
663 | lockAspectRatio
|
664 | enable={ {
|
665 | top: false,
|
666 | right: showRightHandle,
|
667 | bottom: true,
|
668 | left: showLeftHandle,
|
669 | } }
|
670 | onResizeStart={ () => {
|
671 | toggleSelection( false );
|
672 | } }
|
673 | onResizeStop={ ( event, direction, elt, delta ) => {
|
674 | setAttributes( {
|
675 | width: parseInt( currentWidth + delta.width, 10 ),
|
676 | height: parseInt( currentHeight + delta.height, 10 ),
|
677 | } );
|
678 | toggleSelection( true );
|
679 | } }
|
680 | >
|
681 | { img }
|
682 | </ResizableBox>
|
683 | </Fragment>
|
684 | );
|
685 | } }
|
686 | </ImageSize>
|
687 | { ( ! RichText.isEmpty( caption ) || isSelected ) && (
|
688 | <RichText
|
689 | tagName="figcaption"
|
690 | placeholder={ __( 'Write caption…' ) }
|
691 | value={ caption }
|
692 | unstableOnFocus={ this.onFocusCaption }
|
693 | onChange={ ( value ) => setAttributes( { caption: value } ) }
|
694 | isSelected={ this.state.captionFocused }
|
695 | inlineToolbar
|
696 | />
|
697 | ) }
|
698 | </figure>
|
699 | </Fragment>
|
700 | );
|
701 |
|
702 | }
|
703 | }
|
704 |
|
705 | export default compose( [
|
706 | withSelect( ( select, props ) => {
|
707 | const { getMedia } = select( 'core' );
|
708 | const { getSettings } = select( 'core/block-editor' );
|
709 | const { id } = props.attributes;
|
710 | const { maxWidth, isRTL, imageSizes } = getSettings();
|
711 |
|
712 | return {
|
713 | image: id ? getMedia( id ) : null,
|
714 | maxWidth,
|
715 | isRTL,
|
716 | imageSizes,
|
717 | };
|
718 | } ),
|
719 | withViewportMatch( { isLargeViewport: 'medium' } ),
|
720 | withNotices,
|
721 | ] )( ImageEdit );
|