Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dropping multiple images to the image block #65030

Merged
merged 3 commits into from
Sep 5, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ Callback called when urls can be configured. No media insertion from url will be

### handleUpload

When set to false the handling of the upload is left to the calling component.
When the value is set to `false` or returned as `false`, the handling of the upload is left to the consumer component. The function signature accepts an array containing the files to be uploaded.

- Type: `Boolean`
- Type: `Boolean|Function`
- Required: No
- Default: `true`
- Platform: Web
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ export function MediaPlaceholder( {
};

const onFilesUpload = ( files ) => {
if ( ! handleUpload ) {
if (
! handleUpload ||
( typeof handleUpload === 'function' && ! handleUpload( files ) )
) {
return onSelect( files );
}
onFilesPreUpload( files );
Expand Down
63 changes: 58 additions & 5 deletions packages/block-library/src/image/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import clsx from 'clsx';
/**
* WordPress dependencies
*/
import { isBlobURL } from '@wordpress/blob';
import { store as blocksStore } from '@wordpress/blocks';
import { isBlobURL, createBlobURL } from '@wordpress/blob';
import { store as blocksStore, createBlock } from '@wordpress/blocks';
import { Placeholder } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import {
Expand All @@ -31,6 +31,7 @@ import { useResizeObserver } from '@wordpress/compose';
import { unlock } from '../lock-unlock';
import { useUploadMediaFromBlobURL } from '../utils/hooks';
import Image from './image';
import { isValidFileType } from './utils';

/**
* Module constants
Expand Down Expand Up @@ -109,6 +110,7 @@ export function ImageEdit( {
metadata,
} = attributes;
const [ temporaryURL, setTemporaryURL ] = useState( attributes.blob );
const figureRef = useRef();

const [ contentResizeListener, { width: containerWidth } ] =
useResizeObserver();
Expand All @@ -123,7 +125,7 @@ export function ImageEdit( {
captionRef.current = caption;
}, [ caption ] );

const { __unstableMarkNextChangeAsNotPersistent } =
const { __unstableMarkNextChangeAsNotPersistent, replaceBlock } =
useDispatch( blockEditorStore );

useEffect( () => {
Expand All @@ -138,7 +140,12 @@ export function ImageEdit( {
}
}, [ __unstableMarkNextChangeAsNotPersistent, align, setAttributes ] );

const { getSettings } = useSelect( blockEditorStore );
const {
getSettings,
getBlockRootClientId,
getBlockName,
canInsertBlockType,
} = useSelect( blockEditorStore );
const blockEditingMode = useBlockEditingMode();

const { createErrorNotice } = useDispatch( noticesStore );
Expand All @@ -152,7 +159,52 @@ export function ImageEdit( {
} );
}

function onSelectImagesList( images ) {
const win = figureRef.current?.ownerDocument.defaultView;

if ( images.every( ( file ) => file instanceof win.File ) ) {
/** @type {File[]} */
const files = images;
const rootClientId = getBlockRootClientId( clientId );

if ( files.some( ( file ) => ! isValidFileType( file ) ) ) {
// Copied from the same notice in the gallery block.
createErrorNotice(
__(
'If uploading to a gallery all files need to be image formats'
),
{ id: 'gallery-upload-invalid-file', type: 'snackbar' }
);
}

const imageBlocks = files
.filter( ( file ) => isValidFileType( file ) )
.map( ( file ) =>
createBlock( 'core/image', {
blob: createBlobURL( file ),
} )
);

if ( getBlockName( rootClientId ) === 'core/gallery' ) {
replaceBlock( clientId, imageBlocks );
} else if ( canInsertBlockType( 'core/gallery', rootClientId ) ) {
const galleryBlock = createBlock(
'core/gallery',
{},
imageBlocks
);

replaceBlock( clientId, galleryBlock );
}
}
}

function onSelectImage( media ) {
if ( Array.isArray( media ) ) {
onSelectImagesList( media );
return;
}

if ( ! media || ! media.url ) {
setAttributes( {
url: undefined,
Expand Down Expand Up @@ -296,7 +348,7 @@ export function ImageEdit( {
Object.keys( borderProps.style ).length > 0 ),
} );

const blockProps = useBlockProps( { className: classes } );
const blockProps = useBlockProps( { ref: figureRef, className: classes } );

// Much of this description is duplicated from MediaPlaceholder.
const { lockUrlControls = false, lockUrlControlsMessage } = useSelect(
Expand Down Expand Up @@ -394,6 +446,7 @@ export function ImageEdit( {
placeholder={ placeholder }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MediaPlaceholder has a multiple prop - is that of any help here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that too. Unfortunately, passing multiple will change the UI of the media library. I think we'll have to refactor the media library to make it not do so when only selecting one image 😅.

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, the media placeholder should be optimized for multiple file uploads.

accept="image/*"
allowedTypes={ ALLOWED_MEDIA_TYPES }
handleUpload={ ( files ) => files.length === 1 }
value={ { id, src } }
mediaPreview={ mediaPreview }
disableMediaButtons={ temporaryURL || url }
Expand Down
14 changes: 13 additions & 1 deletion packages/block-library/src/image/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
import { NEW_TAB_REL } from './constants';
import { NEW_TAB_REL, ALLOWED_MEDIA_TYPES } from './constants';

/**
* Evaluates a CSS aspect-ratio property value as a number.
Expand Down Expand Up @@ -81,3 +81,15 @@ export function getImageSizeAttributes( image, size ) {

return {};
}

/**
* Checks if the file has a valid file type.
*
* @param {File} file - The file to check.
* @return {boolean} - Returns true if the file has a valid file type, otherwise false.
*/
export function isValidFileType( file ) {
return ALLOWED_MEDIA_TYPES.some(
( mediaType ) => file.type.indexOf( mediaType ) === 0
);
}
Loading