diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index d972aeca06bd7..bdfdb7a45dc4d 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -1,7 +1,14 @@ /** * External dependencies */ -import { every, get, noop, startsWith, defaultTo } from 'lodash'; +import { + defaultTo, + every, + get, + isArray, + noop, + startsWith, +} from 'lodash'; import classnames from 'classnames'; /** @@ -103,8 +110,28 @@ export class MediaPlaceholder extends Component { } onFilesUpload( files ) { - const { onSelect, multiple, onError, allowedTypes, mediaUpload } = this.props; - const setMedia = multiple ? onSelect : ( [ media ] ) => onSelect( media ); + const { + addToGallery, + allowedTypes, + mediaUpload, + multiple, + onError, + onSelect, + value = [], + } = this.props; + let setMedia; + if ( multiple ) { + if ( addToGallery ) { + const currentValue = value; + setMedia = ( newMedia ) => { + onSelect( currentValue.concat( newMedia ) ); + }; + } else { + setMedia = onSelect; + } + } else { + setMedia = ( [ media ] ) => onSelect( media ); + } mediaUpload( { allowedTypes, filesList: files, @@ -124,18 +151,21 @@ export class MediaPlaceholder extends Component { render() { const { accept, - icon, + addToGallery, + allowedTypes = [], className, + dropZoneUIOnly, + hasUploadPermissions, + icon, + isAppender, labels = {}, - onSelect, - value = {}, - onSelectURL, - onHTMLDrop = noop, + mediaUpload, multiple = false, notices, - allowedTypes = [], - hasUploadPermissions, - mediaUpload, + onHTMLDrop = noop, + onSelect, + onSelectURL, + value = {}, } = this.props; const { @@ -143,20 +173,20 @@ export class MediaPlaceholder extends Component { src, } = this.state; - let instructions = labels.instructions || ''; - let title = labels.title || ''; + let instructions = labels.instructions; + let title = labels.title; if ( ! hasUploadPermissions && ! onSelectURL ) { instructions = __( 'To edit this block, you need permission to upload media.' ); } - if ( ! instructions || ! title ) { + if ( instructions === undefined || title === undefined ) { const isOneType = 1 === allowedTypes.length; const isAudio = isOneType && 'audio' === allowedTypes[ 0 ]; const isImage = isOneType && 'image' === allowedTypes[ 0 ]; const isVideo = isOneType && 'video' === allowedTypes[ 0 ]; - if ( ! instructions ) { + if ( instructions === undefined ) { if ( hasUploadPermissions ) { instructions = __( 'Drag a media file, upload a new one or select a file from your library.' ); @@ -180,7 +210,7 @@ export class MediaPlaceholder extends Component { } } - if ( ! title ) { + if ( title === undefined ) { title = __( 'Media' ); if ( isAudio ) { @@ -193,24 +223,47 @@ export class MediaPlaceholder extends Component { } } + const dropZone = ( + + ); + + if ( dropZoneUIOnly ) { + return ( + + { dropZone } + + ); + } + return ( { !! mediaUpload && ( - + { dropZone } ) } ( - - ) } + value={ + isArray( value ) ? + value.map( ( { id } ) => id ) : + value.id + } + render={ ( { open } ) => { + return ( + + ); + } } /> { onSelectURL && ( diff --git a/packages/block-editor/src/components/media-placeholder/style.scss b/packages/block-editor/src/components/media-placeholder/style.scss index 7cc50c523885a..15ee92a684e6d 100644 --- a/packages/block-editor/src/components/media-placeholder/style.scss +++ b/packages/block-editor/src/components/media-placeholder/style.scss @@ -45,3 +45,23 @@ .components-form-file-upload .block-editor-media-placeholder__button { margin-right: $grid-size-small; } + +.block-editor-media-placeholder.is-appender { + min-height: 100px; + background-color: unset; + outline: $border-width dashed $dark-gray-150; + + &:hover { + outline: $border-width dashed $dark-gray-500; + } + + .block-editor-media-placeholder__upload-button { + margin-right: $grid-size-small; + &.components-button:hover, + &.components-button:focus { + box-shadow: none; + border: $border-width solid $dark-gray-500; + } + } + +} diff --git a/packages/block-editor/src/components/media-upload/README.md b/packages/block-editor/src/components/media-upload/README.md index eb2f75cbdd8af..6e0a67d9f187c 100644 --- a/packages/block-editor/src/components/media-upload/README.md +++ b/packages/block-editor/src/components/media-upload/README.md @@ -101,6 +101,25 @@ CSS class added to the media modal frame. - Type: `String` - Required: No + +### addToGallery + +If true, the gallery media modal opens directly in the media library where the user can add additional images. +If false the gallery media modal opens in the edit mode where the user can edit existing images, by reordering them, remove them, or change their attributes. +Only applies if `gallery === true`. + +- Type: `Boolean` +- Required: No +- Default: `false` + +### gallery + +If true, the component will initiate all the states required to represent a gallery. By default, the media modal opens in the gallery edit frame, but that can be changed using the `addToGallery`flag. + +- Type: `Boolean` +- Required: No +- Default: `false` + ## render A callback invoked to render the Button opening the media library. diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 868a6a083723d..113fceed04e74 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -8,8 +8,6 @@ import { filter, pick, map, get } from 'lodash'; * WordPress dependencies */ import { - DropZone, - FormFileUpload, IconButton, PanelBody, RangeControl, @@ -25,7 +23,6 @@ import { MediaUpload, InspectorControls, } from '@wordpress/block-editor'; -import { mediaUpload } from '@wordpress/editor'; import { Component, Fragment } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; @@ -64,8 +61,6 @@ class GalleryEdit extends Component { this.toggleImageCrop = this.toggleImageCrop.bind( this ); this.onRemoveImage = this.onRemoveImage.bind( this ); this.setImageAttributes = this.setImageAttributes.bind( this ); - this.addFiles = this.addFiles.bind( this ); - this.uploadFromFiles = this.uploadFromFiles.bind( this ); this.setAttributes = this.setAttributes.bind( this ); this.state = { @@ -152,27 +147,6 @@ class GalleryEdit extends Component { } ); } - uploadFromFiles( event ) { - this.addFiles( event.target.files ); - } - - addFiles( files ) { - const currentImages = this.props.attributes.images || []; - const { noticeOperations } = this.props; - const { setAttributes } = this; - mediaUpload( { - allowedTypes: ALLOWED_MEDIA_TYPES, - filesList: files, - onFileChange: ( images ) => { - const imagesNormalized = images.map( ( image ) => pickRelevantMediaFiles( image ) ); - setAttributes( { - images: currentImages.concat( imagesNormalized ), - } ); - }, - onError: noticeOperations.createErrorNotice, - } ); - } - componentDidUpdate( prevProps ) { // Deselect images when deselecting the block if ( ! this.props.isSelected && prevProps.isSelected ) { @@ -187,15 +161,11 @@ class GalleryEdit extends Component { const { attributes, isSelected, className, noticeOperations, noticeUI } = this.props; const { images, columns = defaultColumnsNumber( attributes ), align, imageCrop, linkTo } = attributes; - const dropZone = ( - - ); + const hasImages = !! images.length; const controls = ( - { !! images.length && ( + { hasImages && ( ); - if ( images.length === 0 ) { + const mediaPlaceholder = ( + } + labels={ { + title: ! hasImages && __( 'Gallery' ), + instructions: ! hasImages && __( 'Drag images, upload new ones or select files from your library.' ), + } } + onSelect={ this.onSelectImages } + accept="image/*" + allowedTypes={ ALLOWED_MEDIA_TYPES } + multiple + value={ hasImages ? images : undefined } + onError={ noticeOperations.createErrorNotice } + notices={ hasImages ? undefined : noticeUI } + /> + ); + + if ( ! hasImages ) { return ( { controls } - } - className={ className } - labels={ { - title: __( 'Gallery' ), - instructions: __( 'Drag images, upload new ones or select files from your library.' ), - } } - onSelect={ this.onSelectImages } - accept="image/*" - allowedTypes={ ALLOWED_MEDIA_TYPES } - multiple - notices={ noticeUI } - onError={ noticeOperations.createErrorNotice } - /> + { mediaPlaceholder } ); } @@ -276,7 +254,6 @@ class GalleryEdit extends Component { } ) } > - { dropZone } { images.map( ( img, index ) => { /* translators: %1$d is the order number of the image, %2$d is the total number of images. */ const ariaLabel = sprintf( __( 'image %1$d of %2$d in gallery' ), ( index + 1 ), images.length ); @@ -297,21 +274,8 @@ class GalleryEdit extends Component { ); } ) } - { isSelected && -
  • - - { __( 'Upload an image' ) } - -
  • - } + { mediaPlaceholder } ); } diff --git a/packages/block-library/src/gallery/editor.scss b/packages/block-library/src/gallery/editor.scss index 0340f66a31e8e..57ed4d07d969e 100644 --- a/packages/block-library/src/gallery/editor.scss +++ b/packages/block-library/src/gallery/editor.scss @@ -53,31 +53,6 @@ ul.wp-block-gallery li { } } - .components-form-file-upload, - .components-button.block-library-gallery-add-item-button { - width: 100%; - height: 100%; - } - - .components-button.block-library-gallery-add-item-button { - display: flex; - flex-direction: column; - justify-content: center; - box-shadow: none; - border: none; - border-radius: 0; - min-height: 100px; - - & .dashicon { - margin-top: 10px; - } - - &:hover, - &:focus { - border: $border-width solid $dark-gray-500; - } - } - .block-editor-rich-text figcaption { a { color: $white; @@ -122,14 +97,3 @@ ul.wp-block-gallery li { margin-top: -9px; margin-left: -9px; } - -// Last item always needs margins reset. -// When block is selected, only reset the right margin of the 2nd to last item. -.wp-block-gallery { - .is-selected & .blocks-gallery-image:nth-last-child(2), - .is-selected & .blocks-gallery-item:nth-last-child(2), - .is-typing & .blocks-gallery-image:nth-last-child(2), - .is-typing & .blocks-gallery-item:nth-last-child(2) { - margin-right: 0; - } -} diff --git a/packages/block-library/src/gallery/style.scss b/packages/block-library/src/gallery/style.scss index 2557f727c5c0e..df60d6fd1fc9b 100644 --- a/packages/block-library/src/gallery/style.scss +++ b/packages/block-library/src/gallery/style.scss @@ -126,14 +126,6 @@ margin-right: 0; } - // Make the "Add new Gallery item" button full-width (so it always appears - // below other items). - .blocks-gallery-item { - &.has-add-item-button { - width: 100%; - } - } - // Apply max-width to floated items that have no intrinsic width. &.alignleft, &.alignright { diff --git a/packages/edit-post/src/hooks/components/media-upload/index.js b/packages/edit-post/src/hooks/components/media-upload/index.js index df65411e1c305..eefb3450a6e1c 100644 --- a/packages/edit-post/src/hooks/components/media-upload/index.js +++ b/packages/edit-post/src/hooks/components/media-upload/index.js @@ -78,10 +78,10 @@ const getAttachmentsCollection = ( ids ) => { class MediaUpload extends Component { constructor( { allowedTypes, - multiple = false, gallery = false, - title = __( 'Select or Upload Media' ), modalClass, + multiple = false, + title = __( 'Select or Upload Media' ), } ) { super( ...arguments ); this.openModal = this.openModal.bind( this ); @@ -124,6 +124,7 @@ class MediaUpload extends Component { buildAndSetGalleryFrame() { const { + addToGallery = false, allowedTypes, multiple = false, value = null, @@ -140,7 +141,12 @@ class MediaUpload extends Component { if ( this.frame ) { this.frame.remove(); } - const currentState = value ? 'gallery-edit' : 'gallery'; + let currentState; + if ( addToGallery ) { + currentState = 'gallery-library'; + } else { + currentState = value ? 'gallery-edit' : 'gallery'; + } if ( ! this.GalleryDetailsMediaFrame ) { this.GalleryDetailsMediaFrame = getGalleryDetailsMediaFrame(); }