From ba879622bcd2cd14f2cd5e0f7a351a9d15ace76e Mon Sep 17 00:00:00 2001 From: Jorge Date: Tue, 6 Feb 2018 16:34:33 +0000 Subject: [PATCH] Add caption to gallery images (#4199) Made gallery to image and image to gallery transformations take into consideration caption attributes. Implemented caption editing and styles in the gallery block. Added caption to the attributes of images in the gallery and to the saving logic. Added caption slimImageObject set of MediaUploadButton. --- blocks/library/gallery/block.js | 33 +++++++++++++++++-- blocks/library/gallery/editor.scss | 20 ++++++++++- blocks/library/gallery/gallery-image.js | 18 +++++++++- blocks/library/gallery/index.js | 15 +++++++-- blocks/library/gallery/style.scss | 16 ++++++++- blocks/media-upload/index.js | 2 +- blocks/test/fixtures/core__gallery.json | 6 ++-- .../test/fixtures/core__gallery__columns.json | 6 ++-- 8 files changed, 102 insertions(+), 14 deletions(-) diff --git a/blocks/library/gallery/block.js b/blocks/library/gallery/block.js index 1e476ec047e93a..49468b12ca2b83 100644 --- a/blocks/library/gallery/block.js +++ b/blocks/library/gallery/block.js @@ -39,6 +39,7 @@ class GalleryBlock extends Component { constructor() { super( ...arguments ); + this.onFocusImageCaption = this.onFocusImageCaption.bind( this ); this.onSelectImage = this.onSelectImage.bind( this ); this.onSelectImages = this.onSelectImages.bind( this ); this.setLinkTo = this.setLinkTo.bind( this ); @@ -55,10 +56,27 @@ class GalleryBlock extends Component { } onSelectImage( index ) { - return () => { + return ( event ) => { + // ignore clicks in the editable caption. + // Without this logic, text operations like selection, select / unselects the images. + if ( event.target.tagName === 'FIGCAPTION' ) { + return; + } this.setState( ( state ) => ( { selectedImage: index === state.selectedImage ? null : index, } ) ); + + // unfocus currently focus editable + this.props.setFocus( { ...this.props.focus, editableIndex: undefined } ); + }; + } + + onFocusImageCaption( index ) { + return ( focusValue ) => { + this.setState( { + selectedImage: index, + } ); + this.props.setFocus( { editableIndex: index, ...focusValue } ); }; } @@ -73,8 +91,13 @@ class GalleryBlock extends Component { }; } - onSelectImages( imgs ) { - this.props.setAttributes( { images: imgs } ); + onSelectImages( images ) { + this.props.setAttributes( { + images: images.map( ( attributes ) => ( { + ...attributes, + caption: attributes.caption ? [ attributes.caption ] : [], + } ) ), + } ); } setLinkTo( value ) { @@ -220,6 +243,10 @@ class GalleryBlock extends Component { onRemove={ this.onRemoveImage( index ) } onClick={ this.onSelectImage( index ) } setAttributes={ ( attrs ) => this.setImageAttributes( index, attrs ) } + caption={ img.caption } + focus={ focus } + onFocus={ this.onFocusImageCaption( index ) } + imageIndex={ index } /> ) ) } diff --git a/blocks/library/gallery/editor.scss b/blocks/library/gallery/editor.scss index 86eb072132be1b..cb9213b2f612ee 100644 --- a/blocks/library/gallery/editor.scss +++ b/blocks/library/gallery/editor.scss @@ -3,7 +3,6 @@ } .blocks-gallery-item { - position: relative; .is-selected { outline: 4px solid $blue-medium-500; @@ -13,6 +12,25 @@ &.is-transient img { @include loading_fade; } + + .blocks-rich-text { + position: absolute; + width: 100%; + } + + // last-of-type is used because RichText creates to figcations when placeholders are visible, + // and in that case only the second one should be targeted. + // Using data-is-placeholder-visible caused blinks because the attributes are not immediately added to the dom. + .blocks-rich-text figcaption:last-of-type { + position: relative; + } + + .is-selected .blocks-rich-text { + width: calc( 100% - 8px ); + left: 4px; + margin-top: -4px; + } + } .blocks-gallery-item__inline-menu { diff --git a/blocks/library/gallery/gallery-image.js b/blocks/library/gallery/gallery-image.js index 79dba7c5f4f978..84995acb086485 100644 --- a/blocks/library/gallery/gallery-image.js +++ b/blocks/library/gallery/gallery-image.js @@ -10,6 +10,11 @@ import { Component } from '@wordpress/element'; import { IconButton, withAPIData, Spinner } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import RichText from '../../rich-text'; + class GalleryImage extends Component { componentWillReceiveProps( { image } ) { if ( image && image.data && ! this.props.url ) { @@ -21,7 +26,7 @@ class GalleryImage extends Component { } render() { - const { url, alt, id, linkTo, link, isSelected, onClick, onRemove } = this.props; + const { url, alt, id, linkTo, link, imageIndex, isSelected, caption, onClick, onRemove, focus, setAttributes, onFocus } = this.props; let href; @@ -56,6 +61,17 @@ class GalleryImage extends Component { } { href ? { img } : img } + { ( caption && caption.length > 0 ) || ( focus && isSelected ) ? ( + setAttributes( { caption: newCaption } ) } + inlineToolbar + /> + ) : null } ); /* eslint-enable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/onclick-has-role, jsx-a11y/click-events-have-key-events */ diff --git a/blocks/library/gallery/index.js b/blocks/library/gallery/index.js index 5f5cba42c2f113..dedd553b4baa10 100644 --- a/blocks/library/gallery/index.js +++ b/blocks/library/gallery/index.js @@ -26,10 +26,11 @@ const blockAttributes = { type: 'array', default: [], source: 'query', - selector: 'ul.wp-block-gallery .blocks-gallery-item img', + selector: 'ul.wp-block-gallery .blocks-gallery-item', query: { url: { source: 'attribute', + selector: 'img', attribute: 'src', }, link: { @@ -38,13 +39,20 @@ const blockAttributes = { }, alt: { source: 'attribute', + selector: 'img', attribute: 'alt', default: '', }, id: { source: 'attribute', + selector: 'img', attribute: 'data-id', }, + caption: { + type: 'array', + source: 'children', + selector: 'figcaption', + }, }, }, columns: { @@ -80,7 +88,7 @@ export const settings = { const validImages = filter( attributes, ( { id, url } ) => id && url ); if ( validImages.length > 0 ) { return createBlock( 'core/gallery', { - images: validImages.map( ( { id, url, alt } ) => ( { id, url, alt } ) ), + images: validImages.map( ( { id, url, alt, caption } ) => ( { id, url, alt, caption } ) ), } ); } return createBlock( 'core/gallery' ); @@ -149,7 +157,7 @@ export const settings = { blocks: [ 'core/image' ], transform: ( { images } ) => { if ( images.length > 0 ) { - return images.map( ( { id, url, alt } ) => createBlock( 'core/image', { id, url, alt } ) ); + return images.map( ( { id, url, alt, caption } ) => createBlock( 'core/image', { id, url, alt, caption } ) ); } return createBlock( 'core/image' ); }, @@ -188,6 +196,7 @@ export const settings = {
  • { href ? { img } : img } + { image.caption && image.caption.length > 0 &&
    { image.caption }
    }
  • ); diff --git a/blocks/library/gallery/style.scss b/blocks/library/gallery/style.scss index 96b4f3d5081196..a392e701fb16a0 100644 --- a/blocks/library/gallery/style.scss +++ b/blocks/library/gallery/style.scss @@ -13,10 +13,14 @@ flex-grow: 1; flex-direction: column; justify-content: center; + position: relative; + figure { - height: 100%; margin: 0; + height: 100%; + display: flex; + align-items: flex-end; } img { @@ -24,6 +28,16 @@ max-width: 100%; height: auto; } + + figcaption { + padding-top: 3px; + color: $white; + text-align: center; + font-size: $default-font-size; + background-color: rgba($color: $black, $alpha: 0.7); + position: absolute; + width: 100%; + } } // Cropped diff --git a/blocks/media-upload/index.js b/blocks/media-upload/index.js index 87fdaca3b48501..3b8031e94856ad 100644 --- a/blocks/media-upload/index.js +++ b/blocks/media-upload/index.js @@ -53,7 +53,7 @@ const getGalleryDetailsMediaFrame = () => { // the media library image object contains numerous attributes // we only need this set to display the image in the library const slimImageObject = ( img ) => { - const attrSet = [ 'sizes', 'mime', 'type', 'subtype', 'id', 'url', 'alt', 'link' ]; + const attrSet = [ 'sizes', 'mime', 'type', 'subtype', 'id', 'url', 'alt', 'link', 'caption' ]; return pick( img, attrSet ); }; diff --git a/blocks/test/fixtures/core__gallery.json b/blocks/test/fixtures/core__gallery.json index 60f85dd327fe71..1f1f20b45b79eb 100644 --- a/blocks/test/fixtures/core__gallery.json +++ b/blocks/test/fixtures/core__gallery.json @@ -8,11 +8,13 @@ "images": [ { "url": "https://cldup.com/uuUqE_dXzy.jpg", - "alt": "title" + "alt": "title", + "caption": [] }, { "url": "http://google.com/hi.png", - "alt": "title" + "alt": "title", + "caption": [] } ], "imageCrop": true, diff --git a/blocks/test/fixtures/core__gallery__columns.json b/blocks/test/fixtures/core__gallery__columns.json index d5444c63be8a19..c6fbbd5ef0cbe7 100644 --- a/blocks/test/fixtures/core__gallery__columns.json +++ b/blocks/test/fixtures/core__gallery__columns.json @@ -8,11 +8,13 @@ "images": [ { "url": "https://cldup.com/uuUqE_dXzy.jpg", - "alt": "title" + "alt": "title", + "caption": [] }, { "url": "http://google.com/hi.png", - "alt": "title" + "alt": "title", + "caption": [] } ], "columns": 1,