From 32635c3841b059798fe72b603fdb2b70fa20716f Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 12 Feb 2021 11:42:50 +1300 Subject: [PATCH 01/11] Add back edit and save methods for v1 content --- packages/block-library/src/gallery/block.json | 51 ++ .../block-library/src/gallery/deprecated.js | 362 +++++++------- packages/block-library/src/gallery/edit-v1.js | 467 ++++++++++++++++++ packages/block-library/src/gallery/edit.js | 6 + .../src/gallery/gallery-image.js | 313 ++++++++++++ packages/block-library/src/gallery/index.js | 2 +- packages/block-library/src/gallery/save-v1.js | 82 +++ packages/block-library/src/gallery/save.js | 5 + 8 files changed, 1106 insertions(+), 182 deletions(-) create mode 100644 packages/block-library/src/gallery/edit-v1.js create mode 100644 packages/block-library/src/gallery/gallery-image.js create mode 100644 packages/block-library/src/gallery/save-v1.js diff --git a/packages/block-library/src/gallery/block.json b/packages/block-library/src/gallery/block.json index 79ae042f660069..68a16fde03b550 100644 --- a/packages/block-library/src/gallery/block.json +++ b/packages/block-library/src/gallery/block.json @@ -3,6 +3,57 @@ "name": "core/gallery", "category": "media", "attributes": { + "images": { + "type": "array", + "default": [], + "source": "query", + "selector": ".blocks-gallery-item", + "query": { + "url": { + "type": "string", + "source": "attribute", + "selector": "img", + "attribute": "src" + }, + "fullUrl": { + "type": "string", + "source": "attribute", + "selector": "img", + "attribute": "data-full-url" + }, + "link": { + "type": "string", + "source": "attribute", + "selector": "img", + "attribute": "data-link" + }, + "alt": { + "type": "string", + "source": "attribute", + "selector": "img", + "attribute": "alt", + "default": "" + }, + "id": { + "type": "string", + "source": "attribute", + "selector": "img", + "attribute": "data-id" + }, + "caption": { + "type": "string", + "source": "html", + "selector": ".blocks-gallery-item__caption" + } + } + }, + "ids": { + "type": "array", + "items": { + "type": "number" + }, + "default": [] + }, "imageUploads": { "type": "array", "default": [], diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js index 41753817dc7837..5d19e56394a301 100644 --- a/packages/block-library/src/gallery/deprecated.js +++ b/packages/block-library/src/gallery/deprecated.js @@ -834,184 +834,184 @@ const v5 = { }, }; -const v6 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: '.blocks-gallery-item', - query: { - url: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - fullUrl: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-full-url', - }, - link: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-link', - }, - alt: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - id: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-id', - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-item__caption', - }, - }, - }, - ids: { - type: 'array', - items: { - type: 'number', - }, - default: [], - }, - columns: { - type: 'number', - minimum: 1, - maximum: 8, - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-caption', - }, - imageCrop: { - type: 'boolean', - default: true, - }, - linkTo: { - type: 'string', - }, - sizeSlug: { - type: 'string', - default: 'large', - }, - }, - supports: { - anchor: true, - align: true, - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - imageCrop, - caption, - linkTo, - } = attributes; - const className = `columns-${ columns } ${ - imageCrop ? 'is-cropped' : '' - }`; - - return ( -
- - { ! RichText.isEmpty( caption ) && ( - - ) } -
- ); - }, - isEligible( { imageCount } ) { - return ! imageCount; - }, - migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { - if ( linkTo === 'post' ) { - linkTo = 'attachment'; - } else if ( linkTo === 'file' ) { - linkTo = 'media'; - } - const imageBlocks = images.map( ( image ) => { - return getImageBlock( image, sizeSlug, linkTo ); - } ); - return [ - { - caption, - columns, - imageCrop, - linkTo, - sizeSlug, - imageCount: imageBlocks.length, - allowResize: false, - isGrouped: true, - }, - imageBlocks, - ]; - }, -}; - -export default [ v6, v5, v4, v3, v2, v1 ]; +// const v6 = { +// attributes: { +// images: { +// type: 'array', +// default: [], +// source: 'query', +// selector: '.blocks-gallery-item', +// query: { +// url: { +// type: 'string', +// source: 'attribute', +// selector: 'img', +// attribute: 'src', +// }, +// fullUrl: { +// type: 'string', +// source: 'attribute', +// selector: 'img', +// attribute: 'data-full-url', +// }, +// link: { +// type: 'string', +// source: 'attribute', +// selector: 'img', +// attribute: 'data-link', +// }, +// alt: { +// type: 'string', +// source: 'attribute', +// selector: 'img', +// attribute: 'alt', +// default: '', +// }, +// id: { +// type: 'string', +// source: 'attribute', +// selector: 'img', +// attribute: 'data-id', +// }, +// caption: { +// type: 'string', +// source: 'html', +// selector: '.blocks-gallery-item__caption', +// }, +// }, +// }, +// ids: { +// type: 'array', +// items: { +// type: 'number', +// }, +// default: [], +// }, +// columns: { +// type: 'number', +// minimum: 1, +// maximum: 8, +// }, +// caption: { +// type: 'string', +// source: 'html', +// selector: '.blocks-gallery-caption', +// }, +// imageCrop: { +// type: 'boolean', +// default: true, +// }, +// linkTo: { +// type: 'string', +// }, +// sizeSlug: { +// type: 'string', +// default: 'large', +// }, +// }, +// supports: { +// anchor: true, +// align: true, +// }, +// save( { attributes } ) { +// const { +// images, +// columns = defaultColumnsNumberV1( attributes ), +// imageCrop, +// caption, +// linkTo, +// } = attributes; +// const className = `columns-${ columns } ${ +// imageCrop ? 'is-cropped' : '' +// }`; + +// return ( +//
+// +// { ! RichText.isEmpty( caption ) && ( +// +// ) } +//
+// ); +// }, +// isEligible( { imageCount } ) { +// return ! imageCount; +// }, +// migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { +// if ( linkTo === 'post' ) { +// linkTo = 'attachment'; +// } else if ( linkTo === 'file' ) { +// linkTo = 'media'; +// } +// const imageBlocks = images.map( ( image ) => { +// return getImageBlock( image, sizeSlug, linkTo ); +// } ); +// return [ +// { +// caption, +// columns, +// imageCrop, +// linkTo, +// sizeSlug, +// imageCount: imageBlocks.length, +// allowResize: false, +// isGrouped: true, +// }, +// imageBlocks, +// ]; +// }, +// }; + +export default [ v5, v4, v3, v2, v1 ]; diff --git a/packages/block-library/src/gallery/edit-v1.js b/packages/block-library/src/gallery/edit-v1.js new file mode 100644 index 00000000000000..5b832a9fb45e76 --- /dev/null +++ b/packages/block-library/src/gallery/edit-v1.js @@ -0,0 +1,467 @@ +/** + * External dependencies + */ +import { + every, + filter, + find, + forEach, + get, + isEmpty, + map, + reduce, + some, + toString, +} from 'lodash'; + +/** + * WordPress dependencies + */ +import { compose } from '@wordpress/compose'; +import { + PanelBody, + SelectControl, + ToggleControl, + withNotices, + RangeControl, +} from '@wordpress/components'; +import { + MediaPlaceholder, + InspectorControls, + useBlockProps, +} from '@wordpress/block-editor'; +import { Platform, useEffect, useState, useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; +import { useDispatch, withSelect } from '@wordpress/data'; +import { withViewportMatch } from '@wordpress/viewport'; +import { View } from '@wordpress/primitives'; + +/** + * Internal dependencies + */ +import { sharedIcon } from './shared-icon'; +import { defaultColumnsNumber, pickRelevantMediaFiles } from './shared'; +import Gallery from './gallery'; +import { + LINK_DESTINATION_ATTACHMENT, + LINK_DESTINATION_MEDIA, + LINK_DESTINATION_NONE, +} from './constants'; + +const MAX_COLUMNS = 8; +const linkOptions = [ + { value: LINK_DESTINATION_ATTACHMENT, label: __( 'Attachment Page' ) }, + { value: LINK_DESTINATION_MEDIA, label: __( 'Media File' ) }, + { value: LINK_DESTINATION_NONE, label: __( 'None' ) }, +]; +const ALLOWED_MEDIA_TYPES = [ 'image' ]; + +const PLACEHOLDER_TEXT = Platform.select( { + web: __( + 'Drag images, upload new ones or select files from your library.' + ), + native: __( 'ADD MEDIA' ), +} ); + +const MOBILE_CONTROL_PROPS_RANGE_CONTROL = Platform.select( { + web: {}, + native: { type: 'stepper' }, +} ); + +export function GalleryEditV1( props ) { + const { + attributes, + isSelected, + noticeUI, + noticeOperations, + mediaUpload, + imageSizes, + resizedImages, + onFocus, + } = props; + const { + columns = defaultColumnsNumber( attributes ), + imageCrop, + images, + linkTo, + sizeSlug, + } = attributes; + const [ selectedImage, setSelectedImage ] = useState(); + const [ attachmentCaptions, setAttachmentCaptions ] = useState(); + const { __unstableMarkNextChangeAsNotPersistent } = useDispatch( + 'core/block-editor' + ); + + function setAttributes( newAttrs ) { + if ( newAttrs.ids ) { + throw new Error( + 'The "ids" attribute should not be changed directly. It is managed automatically when "images" attribute changes' + ); + } + + if ( newAttrs.images ) { + newAttrs = { + ...newAttrs, + // Unlike images[ n ].id which is a string, always ensure the + // ids array contains numbers as per its attribute type. + ids: map( newAttrs.images, ( { id } ) => parseInt( id, 10 ) ), + }; + } + + props.setAttributes( newAttrs ); + } + + function onSelectImage( index ) { + return () => { + setSelectedImage( index ); + }; + } + + function onDeselectImage() { + return () => { + setSelectedImage(); + }; + } + + function onMove( oldIndex, newIndex ) { + const newImages = [ ...images ]; + newImages.splice( newIndex, 1, images[ oldIndex ] ); + newImages.splice( oldIndex, 1, images[ newIndex ] ); + setSelectedImage( newIndex ); + setAttributes( { images: newImages } ); + } + + function onMoveForward( oldIndex ) { + return () => { + if ( oldIndex === images.length - 1 ) { + return; + } + onMove( oldIndex, oldIndex + 1 ); + }; + } + + function onMoveBackward( oldIndex ) { + return () => { + if ( oldIndex === 0 ) { + return; + } + onMove( oldIndex, oldIndex - 1 ); + }; + } + + function onRemoveImage( index ) { + return () => { + const newImages = filter( images, ( img, i ) => index !== i ); + setSelectedImage(); + setAttributes( { + images: newImages, + columns: attributes.columns + ? Math.min( newImages.length, attributes.columns ) + : attributes.columns, + } ); + }; + } + + function selectCaption( newImage ) { + // The image id in both the images and attachmentCaptions arrays is a + // string, so ensure comparison works correctly by converting the + // newImage.id to a string. + const newImageId = toString( newImage.id ); + const currentImage = find( images, { id: newImageId } ); + const currentImageCaption = currentImage + ? currentImage.caption + : newImage.caption; + + if ( ! attachmentCaptions ) { + return currentImageCaption; + } + + const attachment = find( attachmentCaptions, { + id: newImageId, + } ); + + // if the attachment caption is updated + if ( attachment && attachment.caption !== newImage.caption ) { + return newImage.caption; + } + + return currentImageCaption; + } + + function onSelectImages( newImages ) { + setAttachmentCaptions( + newImages.map( ( newImage ) => ( { + // Store the attachmentCaption id as a string for consistency + // with the type of the id in the images attribute. + id: toString( newImage.id ), + caption: newImage.caption, + } ) ) + ); + setAttributes( { + images: newImages.map( ( newImage ) => ( { + ...pickRelevantMediaFiles( newImage, sizeSlug ), + caption: selectCaption( newImage, images, attachmentCaptions ), + // The id value is stored in a data attribute, so when the + // block is parsed it's converted to a string. Converting + // to a string here ensures it's type is consistent. + id: toString( newImage.id ), + } ) ), + columns: attributes.columns + ? Math.min( newImages.length, attributes.columns ) + : attributes.columns, + } ); + } + + function onUploadError( message ) { + noticeOperations.removeAllNotices(); + noticeOperations.createErrorNotice( message ); + } + + function setLinkTo( value ) { + setAttributes( { linkTo: value } ); + } + + function setColumnsNumber( value ) { + setAttributes( { columns: value } ); + } + + function toggleImageCrop() { + setAttributes( { imageCrop: ! imageCrop } ); + } + + function getImageCropHelp( checked ) { + return checked + ? __( 'Thumbnails are cropped to align.' ) + : __( 'Thumbnails are not cropped.' ); + } + + function onFocusGalleryCaption() { + setSelectedImage(); + } + + function setImageAttributes( index, newAttributes ) { + if ( ! images[ index ] ) { + return; + } + + setAttributes( { + images: [ + ...images.slice( 0, index ), + { + ...images[ index ], + ...newAttributes, + }, + ...images.slice( index + 1 ), + ], + } ); + } + + function getImagesSizeOptions() { + return map( + filter( imageSizes, ( { slug } ) => + some( resizedImages, ( sizes ) => sizes[ slug ] ) + ), + ( { name, slug } ) => ( { value: slug, label: name } ) + ); + } + + function updateImagesSize( newSizeSlug ) { + const updatedImages = map( images, ( image ) => { + if ( ! image.id ) { + return image; + } + const url = get( resizedImages, [ + parseInt( image.id, 10 ), + newSizeSlug, + ] ); + return { + ...image, + ...( url && { url } ), + }; + } ); + + setAttributes( { images: updatedImages, sizeSlug: newSizeSlug } ); + } + + useEffect( () => { + if ( + Platform.OS === 'web' && + images && + images.length > 0 && + every( images, ( { url } ) => isBlobURL( url ) ) + ) { + const filesList = map( images, ( { url } ) => getBlobByURL( url ) ); + forEach( images, ( { url } ) => revokeBlobURL( url ) ); + mediaUpload( { + filesList, + onFileChange: onSelectImages, + allowedTypes: [ 'image' ], + } ); + } + }, [] ); + + useEffect( () => { + // Deselect images when deselecting the block + if ( ! isSelected ) { + setSelectedImage(); + } + }, [ isSelected ] ); + + useEffect( () => { + // linkTo attribute must be saved so blocks don't break when changing + // image_default_link_type in options.php + if ( ! linkTo ) { + __unstableMarkNextChangeAsNotPersistent(); + setAttributes( { + linkTo: + window?.wp?.media?.view?.settings?.defaultProps?.link || + LINK_DESTINATION_NONE, + } ); + } + }, [ linkTo ] ); + + const hasImages = !! images.length; + + const mediaPlaceholder = ( + + ); + + const blockProps = useBlockProps(); + + if ( ! hasImages ) { + return { mediaPlaceholder }; + } + + const imageSizeOptions = getImagesSizeOptions(); + const shouldShowSizeOptions = hasImages && ! isEmpty( imageSizeOptions ); + + return ( + <> + + + { images.length > 1 && ( + + ) } + + + { shouldShowSizeOptions && ( + + ) } + + + { noticeUI } + + + ); +} + +export default compose( [ + withSelect( ( select, { attributes: { ids }, isSelected } ) => { + const { getMedia } = select( 'core' ); + const { getSettings } = select( 'core/block-editor' ); + const { imageSizes, mediaUpload } = getSettings(); + + const resizedImages = useMemo( () => { + if ( isSelected ) { + return reduce( + ids, + ( currentResizedImages, id ) => { + if ( ! id ) { + return currentResizedImages; + } + const image = getMedia( id ); + const sizes = reduce( + imageSizes, + ( currentSizes, size ) => { + const defaultUrl = get( image, [ + 'sizes', + size.slug, + 'url', + ] ); + const mediaDetailsUrl = get( image, [ + 'media_details', + 'sizes', + size.slug, + 'source_url', + ] ); + return { + ...currentSizes, + [ size.slug ]: + defaultUrl || mediaDetailsUrl, + }; + }, + {} + ); + return { + ...currentResizedImages, + [ parseInt( id, 10 ) ]: sizes, + }; + }, + {} + ); + } + return {}; + }, [ isSelected, ids, imageSizes ] ); + + return { + imageSizes, + mediaUpload, + resizedImages, + }; + } ), + withNotices, + withViewportMatch( { isNarrow: '< small' } ), +] )( GalleryEditV1 ); diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index b086fa8ff2f26f..05045c20e1c26c 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -45,6 +45,7 @@ import { } from './constants'; import useImageSizes from './use-image-sizes'; import useShortCodeTransform from './use-short-code-transform'; +import GalleryEditV1 from './edit-v1'; const MAX_COLUMNS = 8; const linkOptions = [ @@ -82,6 +83,11 @@ function GalleryEdit( props ) { insertBlocksAfter, } = props; + if ( attributes?.ids?.length > 0 ) { + console.log( 'here' ); + return ; + } + const { imageCount, columns = defaultColumnsNumber( imageCount ), diff --git a/packages/block-library/src/gallery/gallery-image.js b/packages/block-library/src/gallery/gallery-image.js new file mode 100644 index 00000000000000..4dac396d289e0e --- /dev/null +++ b/packages/block-library/src/gallery/gallery-image.js @@ -0,0 +1,313 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import { get, omit } from 'lodash'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { Button, Spinner, ButtonGroup } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { BACKSPACE, DELETE } from '@wordpress/keycodes'; +import { withSelect, withDispatch } from '@wordpress/data'; +import { RichText, MediaPlaceholder } from '@wordpress/block-editor'; +import { isBlobURL } from '@wordpress/blob'; +import { compose } from '@wordpress/compose'; +import { + closeSmall, + chevronLeft, + chevronRight, + edit, + image as imageIcon, +} from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { pickRelevantMediaFiles } from './shared'; +import { + LINK_DESTINATION_ATTACHMENT, + LINK_DESTINATION_MEDIA, +} from './constants'; + +const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url ); + +class GalleryImage extends Component { + constructor() { + super( ...arguments ); + + this.onSelectImage = this.onSelectImage.bind( this ); + this.onSelectCaption = this.onSelectCaption.bind( this ); + this.onRemoveImage = this.onRemoveImage.bind( this ); + this.bindContainer = this.bindContainer.bind( this ); + this.onEdit = this.onEdit.bind( this ); + this.onSelectImageFromLibrary = this.onSelectImageFromLibrary.bind( + this + ); + this.onSelectCustomURL = this.onSelectCustomURL.bind( this ); + this.state = { + captionSelected: false, + isEditing: false, + }; + } + + bindContainer( ref ) { + this.container = ref; + } + + onSelectCaption() { + if ( ! this.state.captionSelected ) { + this.setState( { + captionSelected: true, + } ); + } + + if ( ! this.props.isSelected ) { + this.props.onSelect(); + } + } + + onSelectImage() { + if ( ! this.props.isSelected ) { + this.props.onSelect(); + } + + if ( this.state.captionSelected ) { + this.setState( { + captionSelected: false, + } ); + } + } + + onRemoveImage( event ) { + if ( + this.container === this.container.ownerDocument.activeElement && + this.props.isSelected && + [ BACKSPACE, DELETE ].indexOf( event.keyCode ) !== -1 + ) { + event.stopPropagation(); + event.preventDefault(); + this.props.onRemove(); + } + } + + onEdit() { + this.setState( { + isEditing: true, + } ); + } + + componentDidUpdate( prevProps ) { + const { + isSelected, + image, + url, + __unstableMarkNextChangeAsNotPersistent, + } = this.props; + if ( image && ! url ) { + __unstableMarkNextChangeAsNotPersistent(); + this.props.setAttributes( { + url: image.source_url, + alt: image.alt_text, + } ); + } + + // unselect the caption so when the user selects other image and comeback + // the caption is not immediately selected + if ( + this.state.captionSelected && + ! isSelected && + prevProps.isSelected + ) { + this.setState( { + captionSelected: false, + } ); + } + } + + deselectOnBlur() { + this.props.onDeselect(); + } + + onSelectImageFromLibrary( media ) { + const { setAttributes, id, url, alt, caption, sizeSlug } = this.props; + if ( ! media || ! media.url ) { + return; + } + + let mediaAttributes = pickRelevantMediaFiles( media, sizeSlug ); + + // If the current image is temporary but an alt text was meanwhile + // written by the user, make sure the text is not overwritten. + if ( isTemporaryImage( id, url ) ) { + if ( alt ) { + mediaAttributes = omit( mediaAttributes, [ 'alt' ] ); + } + } + + // If a caption text was meanwhile written by the user, + // make sure the text is not overwritten by empty captions. + if ( caption && ! get( mediaAttributes, [ 'caption' ] ) ) { + mediaAttributes = omit( mediaAttributes, [ 'caption' ] ); + } + + setAttributes( mediaAttributes ); + this.setState( { + isEditing: false, + } ); + } + + onSelectCustomURL( newURL ) { + const { setAttributes, url } = this.props; + if ( newURL !== url ) { + setAttributes( { + url: newURL, + id: undefined, + } ); + this.setState( { + isEditing: false, + } ); + } + } + + render() { + const { + url, + alt, + id, + linkTo, + link, + isFirstItem, + isLastItem, + isSelected, + caption, + onRemove, + onMoveForward, + onMoveBackward, + setAttributes, + 'aria-label': ariaLabel, + } = this.props; + const { isEditing } = this.state; + + let href; + + switch ( linkTo ) { + case LINK_DESTINATION_MEDIA: + href = url; + break; + case LINK_DESTINATION_ATTACHMENT: + href = link; + break; + } + + const img = ( + // Disable reason: Image itself is not meant to be interactive, but should + // direct image selection and unfocus caption fields. + /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ + <> + { + { isBlobURL( url ) && } + + /* eslint-enable jsx-a11y/no-noninteractive-element-interactions */ + ); + + const className = classnames( { + 'is-selected': isSelected, + 'is-transient': isBlobURL( url ), + } ); + + return ( +
+ { ! isEditing && ( href ? { img } : img ) } + { isEditing && ( + + ) } + +
+ ); + } +} + +export default compose( [ + withSelect( ( select, ownProps ) => { + const { getMedia } = select( 'core' ); + const { id } = ownProps; + + return { + image: id ? getMedia( parseInt( id, 10 ) ) : null, + }; + } ), + withDispatch( ( dispatch ) => { + const { __unstableMarkNextChangeAsNotPersistent } = dispatch( + 'core/block-editor' + ); + return { + __unstableMarkNextChangeAsNotPersistent, + }; + } ), +] )( GalleryImage ); diff --git a/packages/block-library/src/gallery/index.js b/packages/block-library/src/gallery/index.js index 4e6431c19dbc03..52c4b0c4f44e83 100644 --- a/packages/block-library/src/gallery/index.js +++ b/packages/block-library/src/gallery/index.js @@ -14,7 +14,7 @@ import save from './save'; import transforms from './transforms'; const { name } = metadata; - +console.log( 'doh!' ); export { metadata, name }; export const settings = { diff --git a/packages/block-library/src/gallery/save-v1.js b/packages/block-library/src/gallery/save-v1.js new file mode 100644 index 00000000000000..dc1e3ca69df3cf --- /dev/null +++ b/packages/block-library/src/gallery/save-v1.js @@ -0,0 +1,82 @@ +/** + * WordPress dependencies + */ +import { RichText, useBlockProps } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { defaultColumnsNumber } from './shared'; +import { + LINK_DESTINATION_ATTACHMENT, + LINK_DESTINATION_MEDIA, +} from './constants'; + +export default function saveV1( { attributes } ) { + console.log( 'did I get here?' ); + const { + images, + columns = 3, // defaultColumnsNumber( attributes ), + imageCrop, + caption, + linkTo, + } = attributes; + const className = `columns-${ columns } ${ imageCrop ? 'is-cropped' : '' }`; + + return ( +
+
    + { images.map( ( image ) => { + let href; + + switch ( linkTo ) { + case LINK_DESTINATION_MEDIA: + href = image.fullUrl || image.url; + break; + case LINK_DESTINATION_ATTACHMENT: + href = image.link; + break; + } + + const img = ( + { + ); + + return ( +
  • +
    + { href ? { img } : img } + { ! RichText.isEmpty( image.caption ) && ( + + ) } +
    +
  • + ); + } ) } +
+ { ! RichText.isEmpty( caption ) && ( + + ) } +
+ ); +} diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js index dfb21461e31dc8..8cceaf7af8b124 100644 --- a/packages/block-library/src/gallery/save.js +++ b/packages/block-library/src/gallery/save.js @@ -7,8 +7,13 @@ import { RichText, useBlockProps, InnerBlocks } from '@wordpress/block-editor'; * Internal dependencies */ import { defaultColumnsNumber } from './shared'; +import saveV1 from './save-v1'; export default function save( { attributes } ) { + console.log( 'save me!', attributes ); + if ( attributes?.ids?.length > 0 ) { + return saveV1( { attributes } ); + } const { imageCount, caption, From fe2d583f6fba1eb56df93c968c498cd48b296293 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 12 Feb 2021 12:43:21 +1300 Subject: [PATCH 02/11] Get basic swap out of save and edit working if ids array contains values --- packages/block-library/src/gallery/edit-v1.js | 2 +- packages/block-library/src/gallery/edit.js | 1 - .../block-library/src/gallery/gallery-v1.js | 121 ++++++++++++++++++ packages/block-library/src/gallery/save-v1.js | 1 - packages/block-library/src/gallery/save.js | 1 - 5 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 packages/block-library/src/gallery/gallery-v1.js diff --git a/packages/block-library/src/gallery/edit-v1.js b/packages/block-library/src/gallery/edit-v1.js index 5b832a9fb45e76..8798fbcafb771d 100644 --- a/packages/block-library/src/gallery/edit-v1.js +++ b/packages/block-library/src/gallery/edit-v1.js @@ -42,7 +42,7 @@ import { View } from '@wordpress/primitives'; */ import { sharedIcon } from './shared-icon'; import { defaultColumnsNumber, pickRelevantMediaFiles } from './shared'; -import Gallery from './gallery'; +import Gallery from './gallery-v1'; import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_MEDIA, diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 05045c20e1c26c..a6378a89009f7f 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -84,7 +84,6 @@ function GalleryEdit( props ) { } = props; if ( attributes?.ids?.length > 0 ) { - console.log( 'here' ); return ; } diff --git a/packages/block-library/src/gallery/gallery-v1.js b/packages/block-library/src/gallery/gallery-v1.js new file mode 100644 index 00000000000000..dc261bbf1e4699 --- /dev/null +++ b/packages/block-library/src/gallery/gallery-v1.js @@ -0,0 +1,121 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { RichText } from '@wordpress/block-editor'; +import { VisuallyHidden } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; +import { createBlock } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import GalleryImage from './gallery-image'; +import { defaultColumnsNumber } from './shared'; + +export const Gallery = ( props ) => { + const { + attributes, + isSelected, + setAttributes, + selectedImage, + mediaPlaceholder, + onMoveBackward, + onMoveForward, + onRemoveImage, + onSelectImage, + onDeselectImage, + onSetImageAttributes, + onFocusGalleryCaption, + insertBlocksAfter, + blockProps, + } = props; + + const { + align, + columns = defaultColumnsNumber( attributes ), + caption, + imageCrop, + images, + } = attributes; + + return ( +
+
    + { images.map( ( img, index ) => { + const ariaLabel = sprintf( + /* translators: 1: the order number of the image. 2: the total number of images. */ + __( 'image %1$d of %2$d in gallery' ), + index + 1, + images.length + ); + + return ( +
  • + + onSetImageAttributes( index, attrs ) + } + caption={ img.caption } + aria-label={ ariaLabel } + sizeSlug={ attributes.sizeSlug } + /> +
  • + ); + } ) } +
+ { mediaPlaceholder } + setAttributes( { caption: value } ) } + inlineToolbar + __unstableOnSplitAtEnd={ () => + insertBlocksAfter( createBlock( 'core/paragraph' ) ) + } + /> +
+ ); +}; + +function RichTextVisibilityHelper( { isHidden, ...richTextProps } ) { + return isHidden ? ( + + ) : ( + + ); +} + +export default Gallery; diff --git a/packages/block-library/src/gallery/save-v1.js b/packages/block-library/src/gallery/save-v1.js index dc1e3ca69df3cf..c1f0147eb0781f 100644 --- a/packages/block-library/src/gallery/save-v1.js +++ b/packages/block-library/src/gallery/save-v1.js @@ -13,7 +13,6 @@ import { } from './constants'; export default function saveV1( { attributes } ) { - console.log( 'did I get here?' ); const { images, columns = 3, // defaultColumnsNumber( attributes ), diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js index 8cceaf7af8b124..29df4737ce318f 100644 --- a/packages/block-library/src/gallery/save.js +++ b/packages/block-library/src/gallery/save.js @@ -10,7 +10,6 @@ import { defaultColumnsNumber } from './shared'; import saveV1 from './save-v1'; export default function save( { attributes } ) { - console.log( 'save me!', attributes ); if ( attributes?.ids?.length > 0 ) { return saveV1( { attributes } ); } From 6632e77d2000a74507445ddfd9f86a61309f6b00 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 12 Feb 2021 17:06:08 +1300 Subject: [PATCH 03/11] fix up columns calculation and broken css --- lib/experiments-page.php | 11 +++ packages/block-library/src/gallery/edit-v1.js | 5 +- .../block-library/src/gallery/editor.scss | 91 +++++++++++++++++++ .../block-library/src/gallery/gallery-v1.js | 4 +- packages/block-library/src/gallery/save-v1.js | 4 +- 5 files changed, 109 insertions(+), 6 deletions(-) diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 50f7632e5221e2..34a5228d7de509 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -62,6 +62,17 @@ function gutenberg_initialize_experiments_settings() { 'id' => 'gutenberg-widgets-in-customizer', ) ); + add_settings_field( + 'gutenberg-gallery-refactor', + __( 'Gallery Refactor', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Enable the refactored gallery block', 'gutenberg' ), + 'id' => 'gutenberg-gallery-refactor', + ) + ); register_setting( 'gutenberg-experiments', 'gutenberg-experiments' diff --git a/packages/block-library/src/gallery/edit-v1.js b/packages/block-library/src/gallery/edit-v1.js index 8798fbcafb771d..1264295d26b432 100644 --- a/packages/block-library/src/gallery/edit-v1.js +++ b/packages/block-library/src/gallery/edit-v1.js @@ -41,7 +41,8 @@ import { View } from '@wordpress/primitives'; * Internal dependencies */ import { sharedIcon } from './shared-icon'; -import { defaultColumnsNumber, pickRelevantMediaFiles } from './shared'; +import { pickRelevantMediaFiles } from './shared'; +import { defaultColumnsNumberV1 } from './deprecated'; import Gallery from './gallery-v1'; import { LINK_DESTINATION_ATTACHMENT, @@ -81,7 +82,7 @@ export function GalleryEditV1( props ) { onFocus, } = props; const { - columns = defaultColumnsNumber( attributes ), + columns = defaultColumnsNumberV1( attributes ), imageCrop, images, linkTo, diff --git a/packages/block-library/src/gallery/editor.scss b/packages/block-library/src/gallery/editor.scss index 4484675123e0b1..4b0c34f0755198 100644 --- a/packages/block-library/src/gallery/editor.scss +++ b/packages/block-library/src/gallery/editor.scss @@ -65,3 +65,94 @@ figure.wp-block-gallery { margin: 0 8px 0 4px; } } +.blocks-gallery-item { + + // Hide the focus outline that otherwise briefly appears when selecting a block. + figure:not(.is-selected):focus, + img:focus { + outline: none; + } + + figure.is-selected { + + &::before { + box-shadow: 0 0 0 $border-width $white inset, 0 0 0 3px var(--wp-admin-theme-color) inset; + content: ""; + // Shown in Windows 10 high contrast mode. + outline: 2px solid transparent; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + } + } + + figure.is-transient img { + opacity: 0.3; + } + + .is-selected .block-library-gallery-item__inline-menu { + display: inline-flex; + } + + .block-editor-media-placeholder { + margin: 0; + height: 100%; + + .components-placeholder__label { + display: flex; + } + } +} + +.block-library-gallery-item__inline-menu { + display: none; + position: absolute; + top: -2px; + margin: $grid-unit-10; + z-index: z-index(".block-library-gallery-item__inline-menu"); + transition: box-shadow 0.2s ease-out; + @include reduce-motion("transition"); + border-radius: $radius-block-ui; + background: $white; + border: $border-width solid $gray-900; + + &:hover { + box-shadow: $shadow-popover; + } + + @include break-small() { + // Use smaller buttons to fit when there are many columns. + .columns-7 &, + .columns-8 & { + padding: $grid-unit-05 / 2; + } + } + + .components-button.has-icon { + &:not(:focus) { + border: none; + box-shadow: none; + } + + @include break-small() { + // Use smaller buttons to fit when there are many columns. + .columns-7 &, + .columns-8 & { + padding: 0; + width: inherit; + height: inherit; + } + } + } + + &.is-left { + left: -2px; + } + + &.is-right { + right: -2px; + } +} \ No newline at end of file diff --git a/packages/block-library/src/gallery/gallery-v1.js b/packages/block-library/src/gallery/gallery-v1.js index dc261bbf1e4699..7c2e52bd648058 100644 --- a/packages/block-library/src/gallery/gallery-v1.js +++ b/packages/block-library/src/gallery/gallery-v1.js @@ -15,7 +15,7 @@ import { createBlock } from '@wordpress/blocks'; * Internal dependencies */ import GalleryImage from './gallery-image'; -import { defaultColumnsNumber } from './shared'; +import { defaultColumnsNumberV1 } from './deprecated'; export const Gallery = ( props ) => { const { @@ -37,7 +37,7 @@ export const Gallery = ( props ) => { const { align, - columns = defaultColumnsNumber( attributes ), + columns = defaultColumnsNumberV1( attributes ), caption, imageCrop, images, diff --git a/packages/block-library/src/gallery/save-v1.js b/packages/block-library/src/gallery/save-v1.js index c1f0147eb0781f..2bf5e3dc01df22 100644 --- a/packages/block-library/src/gallery/save-v1.js +++ b/packages/block-library/src/gallery/save-v1.js @@ -6,7 +6,7 @@ import { RichText, useBlockProps } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { defaultColumnsNumber } from './shared'; +import { defaultColumnsNumberV1 } from './deprecated'; import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_MEDIA, @@ -15,7 +15,7 @@ import { export default function saveV1( { attributes } ) { const { images, - columns = 3, // defaultColumnsNumber( attributes ), + columns = defaultColumnsNumberV1( attributes ), imageCrop, caption, linkTo, From 8b0513fcc9158e630267ad927a1a33d86e8cd411 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 12 Feb 2021 17:12:35 +1300 Subject: [PATCH 04/11] Add comment to removed deprecation. --- .../block-library/src/gallery/deprecated.js | 359 +++++++++--------- 1 file changed, 180 insertions(+), 179 deletions(-) diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js index 5d19e56394a301..a815f58d7d35dd 100644 --- a/packages/block-library/src/gallery/deprecated.js +++ b/packages/block-library/src/gallery/deprecated.js @@ -834,184 +834,185 @@ const v5 = { }, }; -// const v6 = { -// attributes: { -// images: { -// type: 'array', -// default: [], -// source: 'query', -// selector: '.blocks-gallery-item', -// query: { -// url: { -// type: 'string', -// source: 'attribute', -// selector: 'img', -// attribute: 'src', -// }, -// fullUrl: { -// type: 'string', -// source: 'attribute', -// selector: 'img', -// attribute: 'data-full-url', -// }, -// link: { -// type: 'string', -// source: 'attribute', -// selector: 'img', -// attribute: 'data-link', -// }, -// alt: { -// type: 'string', -// source: 'attribute', -// selector: 'img', -// attribute: 'alt', -// default: '', -// }, -// id: { -// type: 'string', -// source: 'attribute', -// selector: 'img', -// attribute: 'data-id', -// }, -// caption: { -// type: 'string', -// source: 'html', -// selector: '.blocks-gallery-item__caption', -// }, -// }, -// }, -// ids: { -// type: 'array', -// items: { -// type: 'number', -// }, -// default: [], -// }, -// columns: { -// type: 'number', -// minimum: 1, -// maximum: 8, -// }, -// caption: { -// type: 'string', -// source: 'html', -// selector: '.blocks-gallery-caption', -// }, -// imageCrop: { -// type: 'boolean', -// default: true, -// }, -// linkTo: { -// type: 'string', -// }, -// sizeSlug: { -// type: 'string', -// default: 'large', -// }, -// }, -// supports: { -// anchor: true, -// align: true, -// }, -// save( { attributes } ) { -// const { -// images, -// columns = defaultColumnsNumberV1( attributes ), -// imageCrop, -// caption, -// linkTo, -// } = attributes; -// const className = `columns-${ columns } ${ -// imageCrop ? 'is-cropped' : '' -// }`; - -// return ( -//
-//
    -// { images.map( ( image ) => { -// let href; - -// switch ( linkTo ) { -// case DEPRECATED_LINK_DESTINATION_MEDIA: -// href = image.fullUrl || image.url; -// break; -// case DEPRECATED_LINK_DESTINATION_ATTACHMENT: -// href = image.link; -// break; -// } - -// const img = ( -// { -// ); - -// return ( -//
  • -//
    -// { href ? ( -// { img } -// ) : ( -// img -// ) } -// { ! RichText.isEmpty( image.caption ) && ( -// -// ) } -//
    -//
  • -// ); -// } ) } -//
-// { ! RichText.isEmpty( caption ) && ( -// -// ) } -//
-// ); -// }, -// isEligible( { imageCount } ) { -// return ! imageCount; -// }, -// migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { -// if ( linkTo === 'post' ) { -// linkTo = 'attachment'; -// } else if ( linkTo === 'file' ) { -// linkTo = 'media'; -// } -// const imageBlocks = images.map( ( image ) => { -// return getImageBlock( image, sizeSlug, linkTo ); -// } ); -// return [ -// { -// caption, -// columns, -// imageCrop, -// linkTo, -// sizeSlug, -// imageCount: imageBlocks.length, -// allowResize: false, -// isGrouped: true, -// }, -// imageBlocks, -// ]; -// }, -// }; +/* Comment out the v6 migration to allow new gallery format to be tested in tandem with existing +const v6 = { + attributes: { + images: { + type: 'array', + default: [], + source: 'query', + selector: '.blocks-gallery-item', + query: { + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + fullUrl: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'data-full-url', + }, + link: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'data-link', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + id: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'data-id', + }, + caption: { + type: 'string', + source: 'html', + selector: '.blocks-gallery-item__caption', + }, + }, + }, + ids: { + type: 'array', + items: { + type: 'number', + }, + default: [], + }, + columns: { + type: 'number', + minimum: 1, + maximum: 8, + }, + caption: { + type: 'string', + source: 'html', + selector: '.blocks-gallery-caption', + }, + imageCrop: { + type: 'boolean', + default: true, + }, + linkTo: { + type: 'string', + }, + sizeSlug: { + type: 'string', + default: 'large', + }, + }, + supports: { + anchor: true, + align: true, + }, + save( { attributes } ) { + const { + images, + columns = defaultColumnsNumberV1( attributes ), + imageCrop, + caption, + linkTo, + } = attributes; + const className = `columns-${ columns } ${ + imageCrop ? 'is-cropped' : '' + }`; + + return ( +
+
    + { images.map( ( image ) => { + let href; + + switch ( linkTo ) { + case DEPRECATED_LINK_DESTINATION_MEDIA: + href = image.fullUrl || image.url; + break; + case DEPRECATED_LINK_DESTINATION_ATTACHMENT: + href = image.link; + break; + } + + const img = ( + { + ); + return ( +
  • +
    + { href ? ( + { img } + ) : ( + img + ) } + { ! RichText.isEmpty( image.caption ) && ( + + ) } +
    +
  • + ); + } ) } +
+ { ! RichText.isEmpty( caption ) && ( + + ) } +
+ ); + }, + isEligible( { imageCount } ) { + return ! imageCount; + }, + migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { + if ( linkTo === 'post' ) { + linkTo = 'attachment'; + } else if ( linkTo === 'file' ) { + linkTo = 'media'; + } + const imageBlocks = images.map( ( image ) => { + return getImageBlock( image, sizeSlug, linkTo ); + } ); + return [ + { + caption, + columns, + imageCrop, + linkTo, + sizeSlug, + imageCount: imageBlocks.length, + allowResize: false, + isGrouped: true, + }, + imageBlocks, + ]; + }, +}; +*/ export default [ v5, v4, v3, v2, v1 ]; From 6a744e1a13af7116473eceea0e2c9c18f9c376ab Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Fri, 12 Feb 2021 20:01:12 +1300 Subject: [PATCH 05/11] Remove console log --- packages/block-library/src/gallery/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/gallery/index.js b/packages/block-library/src/gallery/index.js index 52c4b0c4f44e83..4e6431c19dbc03 100644 --- a/packages/block-library/src/gallery/index.js +++ b/packages/block-library/src/gallery/index.js @@ -14,7 +14,7 @@ import save from './save'; import transforms from './transforms'; const { name } = metadata; -console.log( 'doh!' ); + export { metadata, name }; export const settings = { From 4116b4904ccafd8e2322566dbb54c5b89601d569 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Mon, 15 Feb 2021 11:59:34 +1300 Subject: [PATCH 06/11] Moved all v1 code to separate dir and revert deprecations --- .../block-library/src/gallery/deprecated.js | 1394 +++++++---------- packages/block-library/src/gallery/edit.js | 2 +- packages/block-library/src/gallery/save.js | 2 +- .../src/gallery/{edit-v1.js => v1/edit.js} | 10 +- .../src/gallery/{ => v1}/gallery-image.js | 4 +- .../gallery/{gallery-v1.js => v1/gallery.js} | 2 +- .../src/gallery/{save-v1.js => v1/save.js} | 4 +- 7 files changed, 556 insertions(+), 862 deletions(-) rename packages/block-library/src/gallery/{edit-v1.js => v1/edit.js} (98%) rename packages/block-library/src/gallery/{ => v1}/gallery-image.js (98%) rename packages/block-library/src/gallery/{gallery-v1.js => v1/gallery.js} (98%) rename packages/block-library/src/gallery/{save-v1.js => v1/save.js} (95%) diff --git a/packages/block-library/src/gallery/deprecated.js b/packages/block-library/src/gallery/deprecated.js index a815f58d7d35dd..f97adbdd300245 100644 --- a/packages/block-library/src/gallery/deprecated.js +++ b/packages/block-library/src/gallery/deprecated.js @@ -7,20 +7,7 @@ import { map, some } from 'lodash'; /** * WordPress dependencies */ -import { RichText, useBlockProps } from '@wordpress/block-editor'; -import { createBlock } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import { - LINK_DESTINATION_ATTACHMENT, - LINK_DESTINATION_MEDIA, - LINK_DESTINATION_NONE, -} from './constants'; - -const DEPRECATED_LINK_DESTINATION_MEDIA = 'file'; -const DEPRECATED_LINK_DESTINATION_ATTACHMENT = 'post'; +import { RichText } from '@wordpress/block-editor'; /** * Original function to determine default number of columns from a block's @@ -35,562 +22,411 @@ export function defaultColumnsNumberV1( attributes ) { return Math.min( 3, attributes.images.length ); } -/** - * Original function to determines new href and linkDestination values for an image block from the - * supplied Gallery link destination. - * - * Used in deprecations: v1-6. - * - * @param {Object} image Gallery image. - * @param {string} destination Gallery's selected link destination. - * @return {Object} New attributes to assign to image block. - */ -export function getHrefAndDestination( image, destination ) { - // Need to determine the URL that the selected destination maps to. - // Gutenberg and WordPress use different constants so the new link - // destination also needs to be tweaked. - switch ( destination ) { - case DEPRECATED_LINK_DESTINATION_MEDIA: - return { - href: image?.source_url || image?.url, // eslint-disable-line camelcase - linkDestination: LINK_DESTINATION_MEDIA, - }; - case DEPRECATED_LINK_DESTINATION_ATTACHMENT: - return { - href: image?.link, - linkDestination: LINK_DESTINATION_ATTACHMENT, - }; - case LINK_DESTINATION_MEDIA: - return { - href: image?.source_url || image?.url, // eslint-disable-line camelcase - linkDestination: LINK_DESTINATION_MEDIA, - }; - case LINK_DESTINATION_ATTACHMENT: - return { - href: image?.link, - linkDestination: LINK_DESTINATION_ATTACHMENT, - }; - case LINK_DESTINATION_NONE: - return { - href: undefined, - linkDestination: LINK_DESTINATION_NONE, - }; - } - - return {}; -} - -/** - * Gets an Image block from gallery image data - * - * Used to migrate Galleries to nested Image InnerBlocks. - * - * @param {Object} image Image properties. - * @param {string} sizeSlug Gallery sizeSlug attribute. - * @param {string} linkTo Gallery linkTo attribute. - * @return {Object} Image block. - */ -export function getImageBlock( image, sizeSlug, linkTo ) { - return createBlock( 'core/image', { - ...( image.id && { id: parseInt( image.id ) } ), - url: image.url, - alt: image.alt, - caption: image.caption, - sizeSlug, - ...getHrefAndDestination( image, linkTo ), - } ); -} -const v1 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: 'div.wp-block-gallery figure.blocks-gallery-image img', - query: { - url: { - source: 'attribute', - attribute: 'src', +const deprecated = [ + { + attributes: { + images: { + type: 'array', + default: [], + source: 'query', + selector: '.blocks-gallery-item', + query: { + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + fullUrl: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'data-full-url', + }, + link: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'data-link', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + id: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'data-id', + }, + caption: { + type: 'string', + source: 'html', + selector: '.blocks-gallery-item__caption', + }, }, - alt: { - source: 'attribute', - attribute: 'alt', - default: '', - }, - id: { - source: 'attribute', - attribute: 'data-id', + }, + ids: { + type: 'array', + items: { + type: 'number', }, + default: [], }, - }, - columns: { - type: 'number', - }, - imageCrop: { - type: 'boolean', - default: true, - }, - linkTo: { - type: 'string', - default: 'none', - }, - align: { - type: 'string', - default: 'none', - }, - }, - supports: { - align: true, - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - align, - imageCrop, - linkTo, - } = attributes; - const className = classnames( `columns-${ columns }`, { - alignnone: align === 'none', - 'is-cropped': imageCrop, - } ); - return ( -
- { images.map( ( image ) => { - let href; - - switch ( linkTo ) { - case 'media': - href = image.url; - break; - case 'attachment': - href = image.link; - break; - } - - const img = ( - { - ); - - return ( -
- { href ? { img } : img } -
- ); - } ) } -
- ); - }, - isEligible( { imageCount } ) { - return ! imageCount; - }, - migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { - const imageBlocks = images.map( ( image ) => { - return getImageBlock( image, sizeSlug, linkTo ); - } ); - return [ - { - caption, - columns, - imageCrop, - linkTo, - sizeSlug, - imageCount: imageBlocks.length, - allowResize: false, - isGrouped: true, + columns: { + type: 'number', + minimum: 1, + maximum: 8, }, - imageBlocks, - ]; - }, -}; - -const v2 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: 'ul.wp-block-gallery .blocks-gallery-item', - query: { - url: { - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - alt: { - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - id: { - source: 'attribute', - selector: 'img', - attribute: 'data-id', - }, - link: { - source: 'attribute', - selector: 'img', - attribute: 'data-link', - }, - caption: { - type: 'array', - source: 'children', - selector: 'figcaption', - }, + caption: { + type: 'string', + source: 'html', + selector: '.blocks-gallery-caption', + }, + imageCrop: { + type: 'boolean', + default: true, + }, + linkTo: { + type: 'string', + default: 'none', + }, + sizeSlug: { + type: 'string', + default: 'large', }, }, - columns: { - type: 'number', - }, - imageCrop: { - type: 'boolean', - default: true, - }, - linkTo: { - type: 'string', - default: 'none', + supports: { + align: true, + }, + isEligible( { linkTo } ) { + return ! linkTo || linkTo === 'attachment' || linkTo === 'media'; + }, + migrate( attributes ) { + let linkTo = attributes.linkTo; + if ( ! attributes.linkTo ) { + linkTo = 'none'; + } else if ( attributes.linkTo === 'attachment' ) { + linkTo = 'post'; + } else if ( attributes.linkTo === 'media' ) { + linkTo = 'file'; + } + return { + ...attributes, + linkTo, + }; }, - }, - isEligible( { images, ids } ) { - return ( - images && - images.length > 0 && - ( ( ! ids && images ) || - ( ids && images && ids.length !== images.length ) || - some( images, ( id, index ) => { - if ( ! id && ids[ index ] !== null ) { - return true; - } - return parseInt( id, 10 ) !== ids[ index ]; - } ) ) - ); - }, - migrate( attributes ) { - return { - ...attributes, - ids: map( attributes.images, ( { id } ) => { - if ( ! id ) { - return null; - } - return parseInt( id, 10 ); - } ), - }; - }, - supports: { - align: true, - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - imageCrop, - linkTo, - } = attributes; - return ( -
    - { images.map( ( image ) => { - let href; - - switch ( linkTo ) { - case 'media': - href = image.url; - break; - case 'attachment': - href = image.link; - break; - } - - const img = ( - { +
      + { images.map( ( image ) => { + let href; + + switch ( linkTo ) { + case 'media': + href = image.fullUrl || image.url; + break; + case 'attachment': + href = image.link; + break; } - /> - ); - return ( -
    • -
      - { href ? { img } : img } - { image.caption && image.caption.length > 0 && ( - - ) } -
      -
    • - ); - } ) } -
    - ); + const img = ( + { + ); + + return ( +
  • +
    + { href ? ( + { img } + ) : ( + img + ) } + { ! RichText.isEmpty( + image.caption + ) && ( + + ) } +
    +
  • + ); + } ) } +
+ { ! RichText.isEmpty( caption ) && ( + + ) } + + ); + }, }, -}; - -const v3 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: 'ul.wp-block-gallery .blocks-gallery-item', - query: { - url: { - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - fullUrl: { - source: 'attribute', - selector: 'img', - attribute: 'data-full-url', - }, - alt: { - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - id: { - source: 'attribute', - selector: 'img', - attribute: 'data-id', - }, - link: { - source: 'attribute', - selector: 'img', - attribute: 'data-link', - }, - caption: { - type: 'array', - source: 'children', - selector: 'figcaption', + { + attributes: { + images: { + type: 'array', + default: [], + source: 'query', + selector: '.blocks-gallery-item', + query: { + url: { + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + fullUrl: { + source: 'attribute', + selector: 'img', + attribute: 'data-full-url', + }, + link: { + source: 'attribute', + selector: 'img', + attribute: 'data-link', + }, + alt: { + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + id: { + source: 'attribute', + selector: 'img', + attribute: 'data-id', + }, + caption: { + type: 'string', + source: 'html', + selector: '.blocks-gallery-item__caption', + }, }, }, + ids: { + type: 'array', + default: [], + }, + columns: { + type: 'number', + }, + caption: { + type: 'string', + source: 'html', + selector: '.blocks-gallery-caption', + }, + imageCrop: { + type: 'boolean', + default: true, + }, + linkTo: { + type: 'string', + default: 'none', + }, }, - ids: { - type: 'array', - default: [], - }, - columns: { - type: 'number', + supports: { + align: true, }, - imageCrop: { - type: 'boolean', - default: true, + isEligible( { ids } ) { + return ids && ids.some( ( id ) => typeof id === 'string' ); }, - linkTo: { - type: 'string', - default: 'none', + migrate( attributes ) { + return { + ...attributes, + ids: map( attributes.ids, ( id ) => { + const parsedId = parseInt( id, 10 ); + return Number.isInteger( parsedId ) ? parsedId : null; + } ), + }; }, - }, - supports: { - align: true, - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - imageCrop, - linkTo, - } = attributes; - return ( -
    - { images.map( ( image ) => { - let href; - - switch ( linkTo ) { - case 'media': - href = image.fullUrl || image.url; - break; - case 'attachment': - href = image.link; - break; - } - - const img = ( - { - ); - - return ( -
  • -
    - { href ? { img } : img } - { image.caption && image.caption.length > 0 && ( - - ) } -
    -
  • - ); - } ) } -
- ); - }, - isEligible( { imageCount } ) { - return ! imageCount; - }, - migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { - const imageBlocks = images.map( ( image ) => { - return getImageBlock( image, sizeSlug, linkTo ); - } ); - - return [ - { - caption, - columns, + save( { attributes } ) { + const { + images, + columns = defaultColumnsNumberV1( attributes ), imageCrop, + caption, linkTo, - sizeSlug, - imageCount: imageBlocks.length, - allowResize: false, - isGrouped: true, - }, - imageBlocks, - ]; - }, -}; + } = attributes; + + return ( +
+
    + { images.map( ( image ) => { + let href; + + switch ( linkTo ) { + case 'media': + href = image.fullUrl || image.url; + break; + case 'attachment': + href = image.link; + break; + } -const v4 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: '.blocks-gallery-item', - query: { - url: { - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - fullUrl: { - source: 'attribute', - selector: 'img', - attribute: 'data-full-url', - }, - link: { - source: 'attribute', - selector: 'img', - attribute: 'data-link', - }, - alt: { - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - id: { - source: 'attribute', - selector: 'img', - attribute: 'data-id', - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-item__caption', + const img = ( + { + ); + + return ( +
  • +
    + { href ? ( + { img } + ) : ( + img + ) } + { ! RichText.isEmpty( + image.caption + ) && ( + + ) } +
    +
  • + ); + } ) } +
+ { ! RichText.isEmpty( caption ) && ( + + ) } +
+ ); + }, + }, + { + attributes: { + images: { + type: 'array', + default: [], + source: 'query', + selector: 'ul.wp-block-gallery .blocks-gallery-item', + query: { + url: { + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + fullUrl: { + source: 'attribute', + selector: 'img', + attribute: 'data-full-url', + }, + alt: { + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + id: { + source: 'attribute', + selector: 'img', + attribute: 'data-id', + }, + link: { + source: 'attribute', + selector: 'img', + attribute: 'data-link', + }, + caption: { + type: 'array', + source: 'children', + selector: 'figcaption', + }, }, }, + ids: { + type: 'array', + default: [], + }, + columns: { + type: 'number', + }, + imageCrop: { + type: 'boolean', + default: true, + }, + linkTo: { + type: 'string', + default: 'none', + }, }, - ids: { - type: 'array', - default: [], - }, - columns: { - type: 'number', - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-caption', - }, - imageCrop: { - type: 'boolean', - default: true, - }, - linkTo: { - type: 'string', - default: 'none', + supports: { + align: true, }, - }, - supports: { - align: true, - }, - isEligible( { ids } ) { - return ids && ids.some( ( id ) => typeof id === 'string' ); - }, - migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { - const imageBlocks = images.map( ( image ) => { - return getImageBlock( image, sizeSlug, linkTo ); - } ); - - return [ - { - caption, - columns, + save( { attributes } ) { + const { + images, + columns = defaultColumnsNumberV1( attributes ), imageCrop, linkTo, - sizeSlug, - imageCount: imageBlocks.length, - allowResize: false, - isGrouped: true, - }, - imageBlocks, - ]; - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - imageCrop, - caption, - linkTo, - } = attributes; - - return ( -
-
    + } = attributes; + return ( +
      { images.map( ( image ) => { let href; @@ -627,159 +463,116 @@ const v4 = { ) : ( img ) } - { ! RichText.isEmpty( image.caption ) && ( - - ) } + { image.caption && + image.caption.length > 0 && ( + + ) }
); } ) } - { ! RichText.isEmpty( caption ) && ( - - ) } - - ); + ); + }, }, -}; - -const v5 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: '.blocks-gallery-item', - query: { - url: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - fullUrl: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-full-url', - }, - link: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-link', - }, - alt: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - id: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-id', - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-item__caption', + { + attributes: { + images: { + type: 'array', + default: [], + source: 'query', + selector: 'ul.wp-block-gallery .blocks-gallery-item', + query: { + url: { + source: 'attribute', + selector: 'img', + attribute: 'src', + }, + alt: { + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + }, + id: { + source: 'attribute', + selector: 'img', + attribute: 'data-id', + }, + link: { + source: 'attribute', + selector: 'img', + attribute: 'data-link', + }, + caption: { + type: 'array', + source: 'children', + selector: 'figcaption', + }, }, }, - }, - ids: { - type: 'array', - items: { + columns: { type: 'number', }, - default: [], - }, - columns: { - type: 'number', - minimum: 1, - maximum: 8, - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-caption', + imageCrop: { + type: 'boolean', + default: true, + }, + linkTo: { + type: 'string', + default: 'none', + }, }, - imageCrop: { - type: 'boolean', - default: true, + isEligible( { images, ids } ) { + return ( + images && + images.length > 0 && + ( ( ! ids && images ) || + ( ids && images && ids.length !== images.length ) || + some( images, ( id, index ) => { + if ( ! id && ids[ index ] !== null ) { + return true; + } + return parseInt( id, 10 ) !== ids[ index ]; + } ) ) + ); }, - linkTo: { - type: 'string', - default: 'none', + migrate( attributes ) { + return { + ...attributes, + ids: map( attributes.images, ( { id } ) => { + if ( ! id ) { + return null; + } + return parseInt( id, 10 ); + } ), + }; }, - sizeSlug: { - type: 'string', - default: 'large', + supports: { + align: true, }, - }, - supports: { - align: true, - }, - isEligible( { linkTo, imageCount } ) { - return ( - ! imageCount && - ( ! linkTo || linkTo === 'attachment' || linkTo === 'media' ) - ); - }, - migrate( attributes ) { - let linkTo = attributes.linkTo; - if ( ! attributes.linkTo ) { - linkTo = 'none'; - } - const imageBlocks = attributes.images.map( ( image ) => { - return getImageBlock( image, attributes.sizeSlug, linkTo ); - } ); - return [ - { - caption: attributes.caption, - columns: attributes.columns, - imageCrop: attributes.imageCrop, + save( { attributes } ) { + const { + images, + columns = defaultColumnsNumberV1( attributes ), + imageCrop, linkTo, - sizeSlug: attributes.sizeSlug, - imageCount: imageBlocks.length, - allowResize: false, - isGrouped: true, - }, - imageBlocks, - ]; - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - imageCrop, - caption, - linkTo, - } = attributes; - - return ( -
-
    + } = attributes; + return ( +
      { images.map( ( image ) => { let href; switch ( linkTo ) { case 'media': - href = image.fullUrl || image.url; + href = image.url; break; case 'attachment': href = image.link; @@ -791,7 +584,6 @@ const v5 = { src={ image.url } alt={ image.alt } data-id={ image.id } - data-full-url={ image.fullUrl } data-link={ image.link } className={ image.id ? `wp-image-${ image.id }` : null @@ -810,133 +602,86 @@ const v5 = { ) : ( img ) } - { ! RichText.isEmpty( image.caption ) && ( - - ) } + { image.caption && + image.caption.length > 0 && ( + + ) }
); } ) } - { ! RichText.isEmpty( caption ) && ( - - ) } - - ); + ); + }, }, -}; - -/* Comment out the v6 migration to allow new gallery format to be tested in tandem with existing -const v6 = { - attributes: { - images: { - type: 'array', - default: [], - source: 'query', - selector: '.blocks-gallery-item', - query: { - url: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'src', - }, - fullUrl: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-full-url', - }, - link: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-link', - }, - alt: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'alt', - default: '', - }, - id: { - type: 'string', - source: 'attribute', - selector: 'img', - attribute: 'data-id', - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-item__caption', + { + attributes: { + images: { + type: 'array', + default: [], + source: 'query', + selector: + 'div.wp-block-gallery figure.blocks-gallery-image img', + query: { + url: { + source: 'attribute', + attribute: 'src', + }, + alt: { + source: 'attribute', + attribute: 'alt', + default: '', + }, + id: { + source: 'attribute', + attribute: 'data-id', + }, }, }, - }, - ids: { - type: 'array', - items: { + columns: { type: 'number', }, - default: [], - }, - columns: { - type: 'number', - minimum: 1, - maximum: 8, - }, - caption: { - type: 'string', - source: 'html', - selector: '.blocks-gallery-caption', - }, - imageCrop: { - type: 'boolean', - default: true, - }, - linkTo: { - type: 'string', + imageCrop: { + type: 'boolean', + default: true, + }, + linkTo: { + type: 'string', + default: 'none', + }, + align: { + type: 'string', + default: 'none', + }, }, - sizeSlug: { - type: 'string', - default: 'large', + supports: { + align: true, }, - }, - supports: { - anchor: true, - align: true, - }, - save( { attributes } ) { - const { - images, - columns = defaultColumnsNumberV1( attributes ), - imageCrop, - caption, - linkTo, - } = attributes; - const className = `columns-${ columns } ${ - imageCrop ? 'is-cropped' : '' - }`; - - return ( -
-
    + save( { attributes } ) { + const { + images, + columns = defaultColumnsNumberV1( attributes ), + align, + imageCrop, + linkTo, + } = attributes; + const className = classnames( `columns-${ columns }`, { + alignnone: align === 'none', + 'is-cropped': imageCrop, + } ); + return ( +
    { images.map( ( image ) => { let href; switch ( linkTo ) { - case DEPRECATED_LINK_DESTINATION_MEDIA: - href = image.fullUrl || image.url; + case 'media': + href = image.url; break; - case DEPRECATED_LINK_DESTINATION_ATTACHMENT: + case 'attachment': href = image.link; break; } @@ -946,73 +691,22 @@ const v6 = { src={ image.url } alt={ image.alt } data-id={ image.id } - data-full-url={ image.fullUrl } - data-link={ image.link } - className={ - image.id ? `wp-image-${ image.id }` : null - } /> ); return ( -
  • -
    - { href ? ( - { img } - ) : ( - img - ) } - { ! RichText.isEmpty( image.caption ) && ( - - ) } -
    -
  • + { href ? { img } : img } +
); } ) } - - { ! RichText.isEmpty( caption ) && ( - - ) } - - ); - }, - isEligible( { imageCount } ) { - return ! imageCount; - }, - migrate( { images, imageCrop, linkTo, sizeSlug, columns, caption } ) { - if ( linkTo === 'post' ) { - linkTo = 'attachment'; - } else if ( linkTo === 'file' ) { - linkTo = 'media'; - } - const imageBlocks = images.map( ( image ) => { - return getImageBlock( image, sizeSlug, linkTo ); - } ); - return [ - { - caption, - columns, - imageCrop, - linkTo, - sizeSlug, - imageCount: imageBlocks.length, - allowResize: false, - isGrouped: true, - }, - imageBlocks, - ]; + + ); + }, }, -}; -*/ -export default [ v5, v4, v3, v2, v1 ]; +]; + +export default deprecated; diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index a6378a89009f7f..17690cd67cb7fe 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -45,7 +45,7 @@ import { } from './constants'; import useImageSizes from './use-image-sizes'; import useShortCodeTransform from './use-short-code-transform'; -import GalleryEditV1 from './edit-v1'; +import GalleryEditV1 from './v1/edit'; const MAX_COLUMNS = 8; const linkOptions = [ diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js index 29df4737ce318f..ed9f66bf35776d 100644 --- a/packages/block-library/src/gallery/save.js +++ b/packages/block-library/src/gallery/save.js @@ -7,7 +7,7 @@ import { RichText, useBlockProps, InnerBlocks } from '@wordpress/block-editor'; * Internal dependencies */ import { defaultColumnsNumber } from './shared'; -import saveV1 from './save-v1'; +import saveV1 from './v1/save'; export default function save( { attributes } ) { if ( attributes?.ids?.length > 0 ) { diff --git a/packages/block-library/src/gallery/edit-v1.js b/packages/block-library/src/gallery/v1/edit.js similarity index 98% rename from packages/block-library/src/gallery/edit-v1.js rename to packages/block-library/src/gallery/v1/edit.js index 1264295d26b432..9de9f6e54c22b3 100644 --- a/packages/block-library/src/gallery/edit-v1.js +++ b/packages/block-library/src/gallery/v1/edit.js @@ -40,15 +40,15 @@ import { View } from '@wordpress/primitives'; /** * Internal dependencies */ -import { sharedIcon } from './shared-icon'; -import { pickRelevantMediaFiles } from './shared'; -import { defaultColumnsNumberV1 } from './deprecated'; -import Gallery from './gallery-v1'; +import { sharedIcon } from '../shared-icon'; +import { pickRelevantMediaFiles } from '../shared'; +import { defaultColumnsNumberV1 } from '../deprecated'; +import Gallery from './gallery'; import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_MEDIA, LINK_DESTINATION_NONE, -} from './constants'; +} from '../constants'; const MAX_COLUMNS = 8; const linkOptions = [ diff --git a/packages/block-library/src/gallery/gallery-image.js b/packages/block-library/src/gallery/v1/gallery-image.js similarity index 98% rename from packages/block-library/src/gallery/gallery-image.js rename to packages/block-library/src/gallery/v1/gallery-image.js index 4dac396d289e0e..b358d845b3599a 100644 --- a/packages/block-library/src/gallery/gallery-image.js +++ b/packages/block-library/src/gallery/v1/gallery-image.js @@ -26,11 +26,11 @@ import { /** * Internal dependencies */ -import { pickRelevantMediaFiles } from './shared'; +import { pickRelevantMediaFiles } from '../shared'; import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_MEDIA, -} from './constants'; +} from '../constants'; const isTemporaryImage = ( id, url ) => ! id && isBlobURL( url ); diff --git a/packages/block-library/src/gallery/gallery-v1.js b/packages/block-library/src/gallery/v1/gallery.js similarity index 98% rename from packages/block-library/src/gallery/gallery-v1.js rename to packages/block-library/src/gallery/v1/gallery.js index 7c2e52bd648058..ee385651d89364 100644 --- a/packages/block-library/src/gallery/gallery-v1.js +++ b/packages/block-library/src/gallery/v1/gallery.js @@ -15,7 +15,7 @@ import { createBlock } from '@wordpress/blocks'; * Internal dependencies */ import GalleryImage from './gallery-image'; -import { defaultColumnsNumberV1 } from './deprecated'; +import { defaultColumnsNumberV1 } from '../deprecated'; export const Gallery = ( props ) => { const { diff --git a/packages/block-library/src/gallery/save-v1.js b/packages/block-library/src/gallery/v1/save.js similarity index 95% rename from packages/block-library/src/gallery/save-v1.js rename to packages/block-library/src/gallery/v1/save.js index 2bf5e3dc01df22..a053f5219be0d6 100644 --- a/packages/block-library/src/gallery/save-v1.js +++ b/packages/block-library/src/gallery/v1/save.js @@ -6,11 +6,11 @@ import { RichText, useBlockProps } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { defaultColumnsNumberV1 } from './deprecated'; +import { defaultColumnsNumberV1 } from '../deprecated'; import { LINK_DESTINATION_ATTACHMENT, LINK_DESTINATION_MEDIA, -} from './constants'; +} from '../constants'; export default function saveV1( { attributes } ) { const { From 95efc3d97f72d94cb51b3c9c54b1c29e7fa8641a Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Mon, 15 Feb 2021 12:44:44 +1300 Subject: [PATCH 07/11] Fix css and image upload issues --- .../src/components/media-placeholder/index.js | 43 ++++++++++++++++++- .../block-library/src/gallery/editor.scss | 22 +++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index 99edaadbd16ec8..0a03a0689f10c1 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -128,7 +128,7 @@ export function MediaPlaceholder( { const onFilesUpload = ( files ) => { if ( isGallery ) { - // Because the Gallery hands the files over to Image component InnerBlocks just + // Because the refactored Gallery hands the files over to Image component InnerBlocks just // hand the handling of the files over to the Gallery onSelect( files ); return; @@ -136,7 +136,46 @@ export function MediaPlaceholder( { onFilesPreUpload( files ); let setMedia; if ( multiple ) { - setMedia = onSelect; + // This is still needed to handle v1 versions of the Gallery block. It can be removed + // once all Gallery instances are forced to migrate. + if ( addToGallery ) { + // Since the setMedia function runs multiple times per upload group + // and is passed newMedia containing every item in its group each time, we must + // filter out whatever this upload group had previously returned to the + // gallery before adding and returning the image array with replacement newMedia + // values. + + // Define an array to store urls from newMedia between subsequent function calls. + let lastMediaPassed = []; + setMedia = ( newMedia ) => { + // Remove any images this upload group is responsible for (lastMediaPassed). + // Their replacements are contained in newMedia. + const filteredMedia = ( value ?? [] ).filter( ( item ) => { + // If Item has id, only remove it if lastMediaPassed has an item with that id. + if ( item.id ) { + return ! lastMediaPassed.some( + // Be sure to convert to number for comparison. + ( { id } ) => Number( id ) === Number( item.id ) + ); + } + // Compare transient images via .includes since gallery may append extra info onto the url. + return ! lastMediaPassed.some( ( { urlSlug } ) => + item.url.includes( urlSlug ) + ); + } ); + // Return the filtered media array along with newMedia. + onSelect( filteredMedia.concat( newMedia ) ); + // Reset lastMediaPassed and set it with ids and urls from newMedia. + lastMediaPassed = newMedia.map( ( media ) => { + // Add everything up to '.fileType' to compare via .includes. + const cutOffIndex = media.url.lastIndexOf( '.' ); + const urlSlug = media.url.slice( 0, cutOffIndex ); + return { id: media.id, urlSlug }; + } ); + }; + } else { + setMedia = onSelect; + } } else { setMedia = ( [ media ] ) => onSelect( media ); } diff --git a/packages/block-library/src/gallery/editor.scss b/packages/block-library/src/gallery/editor.scss index 4b0c34f0755198..7b28a6f77d0e09 100644 --- a/packages/block-library/src/gallery/editor.scss +++ b/packages/block-library/src/gallery/editor.scss @@ -5,11 +5,13 @@ figure.wp-block-gallery { display: block; margin: 0; - - .components-drop-zone { - display: none; - pointer-events: none; + &.has-nested-images { + .components-drop-zone { + display: none; + pointer-events: none; + } } + > .blocks-gallery-caption { flex: 0 0 100%; } @@ -65,8 +67,12 @@ figure.wp-block-gallery { margin: 0 8px 0 4px; } } -.blocks-gallery-item { +/** + * Deprecated css past this point. This can be removed once all galleries are migrated + * to V2. + */ +.blocks-gallery-item { // Hide the focus outline that otherwise briefly appears when selecting a block. figure:not(.is-selected):focus, img:focus { @@ -155,4 +161,10 @@ figure.wp-block-gallery { &.is-right { right: -2px; } +} + +.wp-block-gallery ul.blocks-gallery-grid { + padding: 0; + // Some themes give all
    default margin instead of padding. + margin: 0; } \ No newline at end of file From 2aaefd5df050eb877b859b6de4241ae1a16326b9 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Mon, 15 Feb 2021 13:24:51 +1300 Subject: [PATCH 08/11] Account for first 2 deprecations that didn't have an ids array --- packages/block-library/src/gallery/edit.js | 2 +- packages/block-library/src/gallery/save.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 17690cd67cb7fe..14e447e8aa9c75 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -83,7 +83,7 @@ function GalleryEdit( props ) { insertBlocksAfter, } = props; - if ( attributes?.ids?.length > 0 ) { + if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { return ; } diff --git a/packages/block-library/src/gallery/save.js b/packages/block-library/src/gallery/save.js index ed9f66bf35776d..242dcabf986ac2 100644 --- a/packages/block-library/src/gallery/save.js +++ b/packages/block-library/src/gallery/save.js @@ -10,7 +10,7 @@ import { defaultColumnsNumber } from './shared'; import saveV1 from './v1/save'; export default function save( { attributes } ) { - if ( attributes?.ids?.length > 0 ) { + if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { return saveV1( { attributes } ); } const { From 7157e3854afd11d077b382b6e3b4266786d42f32 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Mon, 15 Feb 2021 13:51:05 +1300 Subject: [PATCH 09/11] Fix linting errors --- .../block-library/src/gallery/edit-wrapper.js | 20 +++++++++++++++++++ packages/block-library/src/gallery/edit.js | 20 ++++++++----------- packages/block-library/src/gallery/index.js | 4 ++-- .../block-library/src/gallery/save-wrapper.js | 18 +++++++++++++++++ packages/block-library/src/gallery/v1/edit.js | 8 +++++--- 5 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 packages/block-library/src/gallery/edit-wrapper.js create mode 100644 packages/block-library/src/gallery/save-wrapper.js diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js new file mode 100644 index 00000000000000..0e5068ed18b48e --- /dev/null +++ b/packages/block-library/src/gallery/edit-wrapper.js @@ -0,0 +1,20 @@ +/** + * Internal dependencies + */ +import GalleryEdit from './edit'; +import GalleryEditV1 from './v1/edit'; + +/* + * Using a wrapper around the logic to load the edit for v1 of Gallery block + * or the refactored version with InnerBlocks. This is to prevent conditional + * use of hooks lint errors if adding this logic to the top of the edit component. + */ +export default function GalleryEditWrapper( props ) { + const { attributes } = props; + + if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { + return ; + } + + return ; +} diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 14e447e8aa9c75..6e45616201337a 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -18,10 +18,12 @@ import { Spinner, } from '@wordpress/components'; import { + store as blockEditorStore, MediaPlaceholder, InspectorControls, useBlockProps, } from '@wordpress/block-editor'; +import { store as coreStore } from '@wordpress/core-data'; import { Platform, useEffect, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; @@ -45,7 +47,6 @@ import { } from './constants'; import useImageSizes from './use-image-sizes'; import useShortCodeTransform from './use-short-code-transform'; -import GalleryEditV1 from './v1/edit'; const MAX_COLUMNS = 8; const linkOptions = [ @@ -83,10 +84,6 @@ function GalleryEdit( props ) { insertBlocksAfter, } = props; - if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { - return ; - } - const { imageCount, columns = defaultColumnsNumber( imageCount ), @@ -102,23 +99,22 @@ function GalleryEdit( props ) { const { __unstableMarkNextChangeAsNotPersistent, replaceInnerBlocks, - } = useDispatch( 'core/block-editor' ); + } = useDispatch( blockEditorStore ); const { getSettings, preferredStyle } = useSelect( ( select ) => { - const settings = select( 'core/block-editor' ).getSettings(); + const settings = select( blockEditorStore ).getSettings(); const preferredStyleVariations = settings.__experimentalPreferredStyleVariations; return { - getBlock: select( 'core/block-editor' ).getBlock, - getSettings: select( 'core/block-editor' ).getSettings, + getBlock: select( blockEditorStore ).getBlock, + getSettings: select( blockEditorStore ).getSettings, preferredStyle: preferredStyleVariations?.value?.[ 'core/image' ], }; }, [] ); const innerBlockImages = useSelect( ( select ) => { - return select( 'core/block-editor' ).getBlock( clientId ) - ?.innerBlocks; + return select( blockEditorStore ).getBlock( clientId )?.innerBlocks; }, [ clientId ] ); @@ -144,7 +140,7 @@ function GalleryEdit( props ) { ) { return imageData; } - const getMedia = select( 'core' ).getMedia; + const getMedia = select( coreStore ).getMedia; const newImageData = innerBlockImages.map( ( imageBlock ) => { return { id: imageBlock.attributes.id, diff --git a/packages/block-library/src/gallery/index.js b/packages/block-library/src/gallery/index.js index 4e6431c19dbc03..5dec7b903183b0 100644 --- a/packages/block-library/src/gallery/index.js +++ b/packages/block-library/src/gallery/index.js @@ -8,9 +8,9 @@ import { gallery as icon } from '@wordpress/icons'; * Internal dependencies */ import deprecated from './deprecated'; -import edit from './edit'; +import edit from './edit-wrapper'; import metadata from './block.json'; -import save from './save'; +import save from './save-wrapper'; import transforms from './transforms'; const { name } = metadata; diff --git a/packages/block-library/src/gallery/save-wrapper.js b/packages/block-library/src/gallery/save-wrapper.js new file mode 100644 index 00000000000000..89926f9e47336b --- /dev/null +++ b/packages/block-library/src/gallery/save-wrapper.js @@ -0,0 +1,18 @@ +/** + * Internal dependencies + */ +import save from './save'; +import saveV1 from './v1/save'; + +/* + * Using a wrapper around the logic to load the save for v1 of Gallery block + * or the refactored version with InnerBlocks. This is to prevent conditional + * use of hooks lint errors if adding this logic to the top of the save component. + */ +export default function saveWrapper( { attributes } ) { + if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { + return saveV1( { attributes } ); + } + + return save( { attributes } ); +} diff --git a/packages/block-library/src/gallery/v1/edit.js b/packages/block-library/src/gallery/v1/edit.js index 9de9f6e54c22b3..15563f51795d79 100644 --- a/packages/block-library/src/gallery/v1/edit.js +++ b/packages/block-library/src/gallery/v1/edit.js @@ -26,6 +26,7 @@ import { RangeControl, } from '@wordpress/components'; import { + store as blockEditorStore, MediaPlaceholder, InspectorControls, useBlockProps, @@ -34,6 +35,7 @@ import { Platform, useEffect, useState, useMemo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { getBlobByURL, isBlobURL, revokeBlobURL } from '@wordpress/blob'; import { useDispatch, withSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; import { withViewportMatch } from '@wordpress/viewport'; import { View } from '@wordpress/primitives'; @@ -91,7 +93,7 @@ export function GalleryEditV1( props ) { const [ selectedImage, setSelectedImage ] = useState(); const [ attachmentCaptions, setAttachmentCaptions ] = useState(); const { __unstableMarkNextChangeAsNotPersistent } = useDispatch( - 'core/block-editor' + blockEditorStore ); function setAttributes( newAttrs ) { @@ -411,8 +413,8 @@ export function GalleryEditV1( props ) { export default compose( [ withSelect( ( select, { attributes: { ids }, isSelected } ) => { - const { getMedia } = select( 'core' ); - const { getSettings } = select( 'core/block-editor' ); + const { getMedia } = select( coreStore ); + const { getSettings } = select( blockEditorStore ); const { imageSizes, mediaUpload } = getSettings(); const resizedImages = useMemo( () => { From 321f758c68ceaa132cea0ad78971c52f9cb2f9e5 Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Mon, 15 Feb 2021 14:23:43 +1300 Subject: [PATCH 10/11] Plumb through experimental flag so new gallery edit and save only load if experiment enabled. --- lib/experiments-page.php | 16 ++++++++++++++++ packages/block-editor/README.md | 1 + packages/block-editor/src/store/defaults.js | 2 ++ .../block-library/src/gallery/edit-wrapper.js | 17 ++++++++++++++++- .../provider/use-block-editor-settings.js | 1 + 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 34a5228d7de509..d2fec837602d7b 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -110,3 +110,19 @@ function gutenberg_display_experiment_section() { $experiments_exist ? array_key_exists( 'gutenberg-gallery-refactor', get_option( 'gutenberg-experiments' ) ) : false, + ); + return array_merge( $settings, $experiments_settings ); +} +add_filter( 'block_editor_settings', 'gutenberg_experiments_editor_settings' ); diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 0515228f27a592..372ec99a5cce45 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -502,6 +502,7 @@ _Properties_ - _\_\_experimentalBlockDirectory_ `boolean`: Whether the user has enabled the Block Directory - _\_\_experimentalBlockPatterns_ `Array`: Array of objects representing the block patterns - _\_\_experimentalBlockPatternCategories_ `Array`: Array of objects representing the block pattern categories +- _\_\_experimentalGalleryRefactor_ `boolean`: Whether the user has enabled the refactored gallery block which uses InnerBlocks # **SkipToSelectedBlock** diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js index ba608d34a35f87..a6c3d565c7d398 100644 --- a/packages/block-editor/src/store/defaults.js +++ b/packages/block-editor/src/store/defaults.js @@ -28,6 +28,7 @@ export const PREFERENCES_DEFAULTS = { * @property {boolean} __experimentalBlockDirectory Whether the user has enabled the Block Directory * @property {Array} __experimentalBlockPatterns Array of objects representing the block patterns * @property {Array} __experimentalBlockPatternCategories Array of objects representing the block pattern categories + * @property {boolean} __experimentalGalleryRefactor Whether the user has enabled the refactored gallery block which uses InnerBlocks */ export const SETTINGS_DEFAULTS = { alignWide: false, @@ -151,6 +152,7 @@ export const SETTINGS_DEFAULTS = { __experimentalBlockPatterns: [], __experimentalBlockPatternCategories: [], __experimentalSpotlightEntityBlocks: [], + __experimentalGalleryRefactor: false, // gradients setting is not used anymore now defaults are passed from theme.json on the server and core has its own defaults. // The setting is only kept for backward compatibility purposes. diff --git a/packages/block-library/src/gallery/edit-wrapper.js b/packages/block-library/src/gallery/edit-wrapper.js index 0e5068ed18b48e..b29b50eeb1e97d 100644 --- a/packages/block-library/src/gallery/edit-wrapper.js +++ b/packages/block-library/src/gallery/edit-wrapper.js @@ -1,3 +1,9 @@ +/** + * WordPress dependencies + */ +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { useSelect } from '@wordpress/data'; + /** * Internal dependencies */ @@ -12,7 +18,16 @@ import GalleryEditV1 from './v1/edit'; export default function GalleryEditWrapper( props ) { const { attributes } = props; - if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { + const __experimentalGalleryRefactor = useSelect( ( select ) => { + const settings = select( blockEditorStore ).getSettings(); + return settings.__experimentalGalleryRefactor; + }, [] ); + + if ( + ! __experimentalGalleryRefactor || + attributes?.ids?.length > 0 || + attributes?.images?.length > 0 + ) { return ; } diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 9e40ec16209498..c787c643797036 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -158,6 +158,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { '__experimentalGlobalStylesBaseStyles', '__experimentalPreferredStyleVariations', '__experimentalSetIsInserterOpened', + '__experimentalGalleryRefactor', 'alignWide', 'allowedBlockTypes', 'availableLegacyWidgets', From ce0ac7e19b44242c3be0f537709ce3a08f2d79dc Mon Sep 17 00:00:00 2001 From: Glen Davies Date: Mon, 15 Feb 2021 14:35:56 +1300 Subject: [PATCH 11/11] Remove save wrapper as not actually needed. --- packages/block-library/src/gallery/index.js | 2 +- .../block-library/src/gallery/save-wrapper.js | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 packages/block-library/src/gallery/save-wrapper.js diff --git a/packages/block-library/src/gallery/index.js b/packages/block-library/src/gallery/index.js index 5dec7b903183b0..224688ddfdd681 100644 --- a/packages/block-library/src/gallery/index.js +++ b/packages/block-library/src/gallery/index.js @@ -10,7 +10,7 @@ import { gallery as icon } from '@wordpress/icons'; import deprecated from './deprecated'; import edit from './edit-wrapper'; import metadata from './block.json'; -import save from './save-wrapper'; +import save from './save'; import transforms from './transforms'; const { name } = metadata; diff --git a/packages/block-library/src/gallery/save-wrapper.js b/packages/block-library/src/gallery/save-wrapper.js deleted file mode 100644 index 89926f9e47336b..00000000000000 --- a/packages/block-library/src/gallery/save-wrapper.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Internal dependencies - */ -import save from './save'; -import saveV1 from './v1/save'; - -/* - * Using a wrapper around the logic to load the save for v1 of Gallery block - * or the refactored version with InnerBlocks. This is to prevent conditional - * use of hooks lint errors if adding this logic to the top of the save component. - */ -export default function saveWrapper( { attributes } ) { - if ( attributes?.ids?.length > 0 || attributes?.images?.length > 0 ) { - return saveV1( { attributes } ); - } - - return save( { attributes } ); -}