diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 4306b855955f52..e8e873c3719d2b 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -1,5 +1,12 @@ ## 2.0.0 (Unreleased) +### New Features + +- Added the `addToGallery` property to the `MediaUpload` interface. The property allows users to open the media modal in the `gallery-library`instead of `gallery-edit` state. +- Added the `addToGallery` property to the `MediaPlaceholder` component. The component passes the property to the `MediaUpload` component used inside the placeholder. +- Added the `isAppender` property to the `MediaPlaceholder` component. The property changes the look of the placeholder to be adequate to scenarios where new files are added to an already existing set of files, e.g., adding files to a gallery. +- Added the `dropZoneUIOnly` property to the `MediaPlaceholder` component. The property makes the `MediaPlaceholder` only render a dropzone without any other additional UI. + ### Breaking Changes - `CopyHandler` will now only catch cut/copy events coming from its `props.children`, instead of from anywhere in the `document`. diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index d972aeca06bd7e..42fa7e7d8fbe71 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, @@ -121,42 +148,32 @@ export class MediaPlaceholder extends Component { this.setState( { isURLInputVisible: false } ); } - render() { + renderPlaceholder( content, onClick ) { const { - accept, - icon, + allowedTypes = [], className, + hasUploadPermissions, + icon, + isAppender, labels = {}, - onSelect, - value = {}, - onSelectURL, - onHTMLDrop = noop, - multiple = false, notices, - allowedTypes = [], - hasUploadPermissions, - mediaUpload, + onSelectURL, } = this.props; - const { - isURLInputVisible, - 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 +197,7 @@ export class MediaPlaceholder extends Component { } } - if ( ! title ) { + if ( title === undefined ) { title = __( 'Media' ); if ( isAudio ) { @@ -193,70 +210,191 @@ export class MediaPlaceholder extends Component { } } + const placeholderClassName = classnames( + 'block-editor-media-placeholder', + 'editor-media-placeholder', + className, + { 'is-appender': isAppender } + ); + return ( - - { !! mediaUpload && ( - - - - { __( 'Upload' ) } - - - ) } - ( - - ) } + { content } + + ); + } + + renderDropZone() { + const { onHTMLDrop = noop } = this.props; + return ( + + ); + } + + renderUrlSelectionUI() { + const { + onSelectURL, + } = this.props; + if ( ! onSelectURL ) { + return null; + } + const { + isURLInputVisible, + src, + } = this.state; + return ( +
+ + { isURLInputVisible && ( + - - { onSelectURL && ( -
+ ) } +
+ ); + } + + renderMediaUploadChecked() { + const { + accept, + addToGallery, + allowedTypes = [], + isAppender, + mediaUpload, + multiple = false, + onSelect, + value = {}, + } = this.props; + + const mediaLibraryButton = ( + id ) : + value.id + } + render={ ( { open } ) => { + return ( - { isURLInputVisible && ( - + ); + } } + /> + ); + + if ( mediaUpload && isAppender ) { + return ( + + { this.renderDropZone() } + { + const content = ( + + + { __( 'Upload' ) } + + { mediaLibraryButton } + { this.renderUrlSelectionUI() } + + ); + return this.renderPlaceholder( content, openFileDialog ); + } } + /> + + ); + } + if ( mediaUpload ) { + const content = ( + + { this.renderDropZone() } + - ) } - + onChange={ this.onUpload } + accept={ accept } + multiple={ multiple } + > + { __( 'Upload' ) } + + { mediaLibraryButton } + { this.renderUrlSelectionUI() } + + ); + return this.renderPlaceholder( content ); + } + return this.renderPlaceholder( mediaLibraryButton ); + } + + render() { + const { + dropZoneUIOnly, + } = this.props; + + if ( dropZoneUIOnly ) { + return ( + + { this.renderDropZone() } + + ); + } + + return ( + + { this.renderMediaUploadChecked() } + ); } } diff --git a/packages/block-editor/src/components/media-placeholder/style.scss b/packages/block-editor/src/components/media-placeholder/style.scss index 7cc50c523885af..d18b42ce5e57a9 100644 --- a/packages/block-editor/src/components/media-placeholder/style.scss +++ b/packages/block-editor/src/components/media-placeholder/style.scss @@ -45,3 +45,30 @@ .components-form-file-upload .block-editor-media-placeholder__button { margin-right: $grid-size-small; } + +.block-editor-media-placeholder.is-appender { + min-height: 100px; + outline: $border-width dashed $dark-gray-150; + + &:hover { + outline: $border-width dashed $dark-gray-500; + cursor: pointer; + } + + .is-dark-theme & { + + &:hover { + outline: $border-width dashed $white; + } + } + + .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 eb2f75cbdd8af2..6e0a67d9f187c6 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 ade7ba978264ba..a42e86fa94037c 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 } ); } @@ -277,7 +255,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 ); @@ -298,21 +275,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 34b08797e2e558..4136018059e499 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; @@ -118,14 +93,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 2557f727c5c0e9..df60d6fd1fc9b6 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/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 2035d92dd9be94..c76f5a75ac8d7d 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -1,5 +1,9 @@ ## 7.2.1 (Unreleased) +### New Features + +- Added a new `render` property to `FormFileUpload` component. Allowing users of the component to custom the UI for their needs. + ### Bug fixes - Fix `instanceId` prop passed through to `Button` component via `MenuItems` producing React console error. Fixed by removing the unnecessary use of `withInstanceId` on the `MenuItems` component [#14599](https://github.com/WordPress/gutenberg/pull/14599) diff --git a/packages/components/src/form-file-upload/index.js b/packages/components/src/form-file-upload/index.js index 461f83c263888c..7a45a075ddfd8c 100644 --- a/packages/components/src/form-file-upload/index.js +++ b/packages/components/src/form-file-upload/index.js @@ -24,10 +24,10 @@ class FormFileUpload extends Component { } render() { - const { children, multiple = false, accept, onChange, icon = 'upload', ...props } = this.props; + const { children, multiple = false, accept, onChange, icon = 'upload', render, ...props } = this.props; - return ( -
    + const ui = render ? + render( { openFileDialog: this.openFileDialog } ) : ( { children } + ); + return ( +
    + { ui } { 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(); }