Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/block-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### Enhancements

- Gallery: Show the gallery's "Add" toolbar control on a selected child Image block, so an image can be added without first selecting the Gallery ([#81469](https://github.com/WordPress/gutenberg/pull/81469)).
- Playlist Track: Use a dedicated icon for the block toolbar. ([#80959](https://github.com/WordPress/gutenberg/pull/80959))
- Playlist: Expose the parent "Add track" toolbar control to selected Playlist Track child blocks via block toolbar sharing ([#80368](https://github.com/WordPress/gutenberg/pull/80368)).
- Playlist: Allow selecting audio tracks individually in the Media Library without holding Shift or Command, transform multiple Audio blocks into a Playlist, and transform a one-track Playlist into Audio. ([#80926](https://github.com/WordPress/gutenberg/pull/80926))
Expand Down
66 changes: 66 additions & 0 deletions packages/block-library/src/gallery/add-images-toolbar-control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
BlockControls,
MediaReplaceFlow,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { ALLOWED_MEDIA_TYPES } from './constants';
import useUpdateImages from './use-update-images';

/**
* The gallery's "Add" toolbar control.
*
* Rendered both by the gallery itself and by a selected child image block, so
* an image can be added without first having to select the gallery. See
* https://github.com/WordPress/gutenberg/issues/47200.
*
* It sits in the `other` group rather than being shared through the
* `__experimentalExposeControlsToChildren` support, which would also share the
* gallery's alignment control — duplicating the image block's own — and move
* the selected image's whole toolbar to the top of the gallery.
*
* @param {Object} props
* @param {string} props.clientId Client ID of the gallery block.
*/
export default function AddImagesToolbarControl( { clientId } ) {
const updateImages = useUpdateImages( clientId );

const { canAddImages, mediaIds } = useSelect(
( select ) => {
const { getBlock, canInsertBlockType } = select( blockEditorStore );

return {
// The same check the inserter makes, so the control is hidden
// wherever an image could not actually be added: a template
// lock on the gallery, a disabled editing mode, a section or
// synced pattern ancestor, an `allowedBlocks` restriction (as
// dynamic galleries set), or preview mode.
canAddImages: canInsertBlockType( 'core/image', clientId ),
mediaIds: ( getBlock( clientId )?.innerBlocks ?? [] )
.map( ( block ) => block.attributes.id )
.filter( ( id ) => !! id ),
};
},
[ clientId ]
);

if ( ! canAddImages ) {
return null;
}

return (
<BlockControls group="other">
<MediaReplaceFlow
allowedTypes={ ALLOWED_MEDIA_TYPES }
handleUpload={ false }
onSelect={ updateImages }
name={ __( 'Add' ) }
multiple
mediaIds={ mediaIds }
addToGallery={ mediaIds.length > 0 }
variant="toolbar"
/>
</BlockControls>
);
}
1 change: 1 addition & 0 deletions packages/block-library/src/gallery/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export const LINK_DESTINATION_ATTACHMENT = 'attachment';
export const LINK_DESTINATION_MEDIA_WP_CORE = 'file';
export const LINK_DESTINATION_ATTACHMENT_WP_CORE = 'post';
export const DEFAULT_MEDIA_SIZE_SLUG = 'large';
export const ALLOWED_MEDIA_TYPES = [ 'image' ];
132 changes: 7 additions & 125 deletions packages/block-library/src/gallery/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@ import {
useInnerBlocksProps,
useBlockEditingMode,
BlockControls,
MediaReplaceFlow,
useSettings,
} from '@wordpress/block-editor';
import { useEffect, useMemo } from '@wordpress/element';
import { __, _x, sprintf } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
import { createBlobURL } from '@wordpress/blob';
import { store as noticesStore } from '@wordpress/notices';
import {
link as linkIcon,
Expand All @@ -51,7 +48,10 @@ import {
LINK_DESTINATION_NONE,
LINK_DESTINATION_LIGHTBOX,
DEFAULT_MEDIA_SIZE_SLUG,
ALLOWED_MEDIA_TYPES,
} from './constants';
import AddImagesToolbarControl from './add-images-toolbar-control';
import useUpdateImages from './use-update-images';
import useImageSizes from './use-image-sizes';
import useGetNewImages from './use-get-new-images';
import useGetMedia from './use-get-media';
Expand Down Expand Up @@ -102,8 +102,6 @@ const NAVIGATION_BUTTON_TYPE_OPTIONS = [
value: 'both',
},
];
const ALLOWED_MEDIA_TYPES = [ 'image' ];

const PLACEHOLDER_TEXT = __(
'Drag and drop images, upload, or choose from your library.'
);
Expand Down Expand Up @@ -160,12 +158,8 @@ export default function GalleryEdit( props ) {
aspectRatio,
} = attributes;

const {
__unstableMarkNextChangeAsNotPersistent,
replaceInnerBlocks,
updateBlockAttributes,
selectBlock,
} = useDispatch( blockEditorStore );
const { __unstableMarkNextChangeAsNotPersistent, updateBlockAttributes } =
useDispatch( blockEditorStore );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

Expand Down Expand Up @@ -345,105 +339,7 @@ export default function GalleryEdit( props ) {
};
}

function isValidFileType( file ) {
const mediaTypeSelector = file.type;

return (
ALLOWED_MEDIA_TYPES.some(
( mediaType ) => mediaTypeSelector?.indexOf( mediaType ) === 0
) || file.blob
);
}

function updateImages( selectedImages ) {
const newFileUploads =
Object.prototype.toString.call( selectedImages ) ===
'[object FileList]';

const imageArray = newFileUploads
? Array.from( selectedImages ).map( ( file ) => {
if ( ! file.url ) {
return {
blob: createBlobURL( file ),
};
}

return file;
} )
: selectedImages;

if ( ! imageArray.every( isValidFileType ) ) {
createErrorNotice(
__(
'If uploading to a gallery all files need to be image formats'
),
{ id: 'gallery-upload-invalid-file', type: 'snackbar' }
);
}

const processedImages = imageArray
.filter( ( file ) => file.url || isValidFileType( file ) )
.map( ( file ) => {
if ( ! file.url ) {
return {
blob: file.blob || createBlobURL( file ),
};
}

return file;
} );

// Because we are reusing existing innerImage blocks any reordering
// done in the media library will be lost so we need to reapply that ordering
// once the new image blocks are merged in with existing.
const newOrderMap = processedImages.reduce(
( result, image, index ) => (
( result[ image.id ] = index ), result
),
{}
);

const existingImageBlocks = ! newFileUploads
? innerBlockImages.filter( ( block ) =>
processedImages.find(
( img ) => img.id === block.attributes.id
)
)
: innerBlockImages;

const newImageList = processedImages.filter(
( img ) =>
! existingImageBlocks.find(
( existingImg ) => img.id === existingImg.attributes.id
)
);

const newBlocks = newImageList.map( ( image ) => {
return createBlock( 'core/image', {
id: image.id,
blob: image.blob,
url: image.url,
caption: image.caption,
alt: image.alt,
} );
} );

replaceInnerBlocks(
clientId,
existingImageBlocks
.concat( newBlocks )
.sort(
( a, b ) =>
newOrderMap[ a.attributes.id ] -
newOrderMap[ b.attributes.id ]
)
);

// Select the first block to scroll into view when new blocks are added.
if ( newBlocks?.length > 0 ) {
selectBlock( newBlocks[ 0 ].clientId );
}
}
const updateImages = useUpdateImages( clientId );

function onUploadError( message ) {
createErrorNotice( message, { type: 'snackbar' } );
Expand Down Expand Up @@ -602,7 +498,6 @@ export default function GalleryEdit( props ) {
}
}, [ linkTo ] );

const hasImageIds = hasImages && images.some( ( image ) => !! image.id );
const imagesUploading = images.some(
( img ) => ! img.id && img.url?.indexOf( 'blob:' ) === 0
);
Expand Down Expand Up @@ -915,20 +810,7 @@ export default function GalleryEdit( props ) {
</BlockControls>
<>
{ ! multiGallerySelection && ! isDynamic && (
<BlockControls group="other">
<MediaReplaceFlow
allowedTypes={ ALLOWED_MEDIA_TYPES }
handleUpload={ false }
onSelect={ updateImages }
name={ __( 'Add' ) }
multiple
mediaIds={ images
.filter( ( image ) => image.id )
.map( ( image ) => image.id ) }
addToGallery={ hasImageIds }
variant="toolbar"
/>
</BlockControls>
<AddImagesToolbarControl clientId={ clientId } />
) }
<GalleryGapCustomProperties
style={ attributes.style }
Expand Down
Loading
Loading