Product image getting removed in the new product editor after replacing cover image (#50181)
* Fix image gallery state conflict with external consumer state * Fix the image gallery removal conflict now that the image gallery is stateless * Add changelog files * Ignore the media if it is replaced by itseft
This commit is contained in:
parent
8849f54491
commit
67ab34aab2
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: enhancement
|
||||||
|
|
||||||
|
Fix image gallery state conflict with external consumer state
|
|
@ -1,13 +1,14 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
|
import type { DragEventHandler } from 'react';
|
||||||
import {
|
import {
|
||||||
|
Children,
|
||||||
createElement,
|
createElement,
|
||||||
cloneElement,
|
cloneElement,
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
useMemo,
|
||||||
} from '@wordpress/element';
|
} from '@wordpress/element';
|
||||||
import { DragEventHandler } from 'react';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { MediaItem, MediaUpload } from '@wordpress/media-utils';
|
import { MediaItem, MediaUpload } from '@wordpress/media-utils';
|
||||||
|
|
||||||
|
@ -15,10 +16,9 @@ import { MediaItem, MediaUpload } from '@wordpress/media-utils';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { moveIndex } from '../sortable';
|
import { moveIndex } from '../sortable';
|
||||||
import { ImageGalleryToolbar } from './index';
|
|
||||||
import { ImageGalleryChild, MediaUploadComponentType } from './types';
|
|
||||||
import { removeItem, replaceItem } from './utils';
|
|
||||||
import { ImageGalleryWrapper } from './image-gallery-wrapper';
|
import { ImageGalleryWrapper } from './image-gallery-wrapper';
|
||||||
|
import { ImageGalleryToolbar } from './index';
|
||||||
|
import type { ImageGalleryChild, MediaUploadComponentType } from './types';
|
||||||
|
|
||||||
export type ImageGalleryProps = {
|
export type ImageGalleryProps = {
|
||||||
children: ImageGalleryChild | ImageGalleryChild[];
|
children: ImageGalleryChild | ImageGalleryChild[];
|
||||||
|
@ -57,26 +57,86 @@ export const ImageGallery: React.FC< ImageGalleryProps > = ( {
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const [ isDragging, setIsDragging ] = useState< boolean >( false );
|
const [ isDragging, setIsDragging ] = useState< boolean >( false );
|
||||||
const [ orderedChildren, setOrderedChildren ] = useState<
|
const childElements = useMemo(
|
||||||
ImageGalleryChild[]
|
() => Children.toArray( children ) as JSX.Element[],
|
||||||
>( [] );
|
[ children ]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect( () => {
|
function cloneChild( child: JSX.Element, childIndex: number ) {
|
||||||
if ( ! children ) {
|
const key = child.key || String( childIndex );
|
||||||
|
const isToolbarVisible = key === activeToolbarKey;
|
||||||
|
|
||||||
|
return cloneElement(
|
||||||
|
child,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
isDraggable: allowDragging && ! child.props.isCover,
|
||||||
|
className: classnames( {
|
||||||
|
'is-toolbar-visible': isToolbarVisible,
|
||||||
|
} ),
|
||||||
|
onClick() {
|
||||||
|
setActiveToolbarKey( isToolbarVisible ? null : key );
|
||||||
|
},
|
||||||
|
onBlur( event: React.FocusEvent< HTMLDivElement > ) {
|
||||||
|
if (
|
||||||
|
isDragging ||
|
||||||
|
event.currentTarget.contains( event.relatedTarget ) ||
|
||||||
|
( event.relatedTarget &&
|
||||||
|
( event.relatedTarget as Element ).closest(
|
||||||
|
'.media-modal, .components-modal__frame'
|
||||||
|
) ) ||
|
||||||
|
( event.relatedTarget &&
|
||||||
|
// Check if not a button within the toolbar is clicked, to prevent hiding the toolbar.
|
||||||
|
( event.relatedTarget as Element ).closest(
|
||||||
|
'.woocommerce-image-gallery__toolbar'
|
||||||
|
) ) ||
|
||||||
|
( event.relatedTarget &&
|
||||||
|
// Prevent toolbar from hiding if the dropdown is clicked within the toolbar.
|
||||||
|
( event.relatedTarget as Element ).closest(
|
||||||
|
'.woocommerce-image-gallery__toolbar-dropdown-popover'
|
||||||
|
) )
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setOrderedChildren(
|
setActiveToolbarKey( null );
|
||||||
( Array.isArray( children ) ? children : [ children ] ).map(
|
},
|
||||||
( child, index ) =>
|
},
|
||||||
cloneElement( child, { key: child.key || String( index ) } )
|
isToolbarVisible && (
|
||||||
|
<ImageGalleryToolbar
|
||||||
|
value={ child.props.id }
|
||||||
|
allowDragging={ allowDragging }
|
||||||
|
childIndex={ childIndex }
|
||||||
|
lastChild={ childIndex === childElements.length - 1 }
|
||||||
|
moveItem={ ( fromIndex: number, toIndex: number ) => {
|
||||||
|
onOrderChange(
|
||||||
|
moveIndex< ImageGalleryChild >(
|
||||||
|
fromIndex,
|
||||||
|
toIndex,
|
||||||
|
childElements
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}, [ children ] );
|
} }
|
||||||
|
removeItem={ ( removeIndex: number ) => {
|
||||||
const updateOrderedChildren = ( items: ImageGalleryChild[] ) => {
|
onRemove( {
|
||||||
setOrderedChildren( items );
|
removeIndex,
|
||||||
onOrderChange( items );
|
removedItem: childElements[ removeIndex ],
|
||||||
};
|
} );
|
||||||
|
} }
|
||||||
|
replaceItem={ (
|
||||||
|
replaceIndex: number,
|
||||||
|
media: { id: number } & MediaItem
|
||||||
|
) => {
|
||||||
|
onReplace( { replaceIndex, media } );
|
||||||
|
} }
|
||||||
|
setToolBarItem={ ( toolBarItem ) => {
|
||||||
|
onSelectAsCover( activeToolbarKey );
|
||||||
|
setActiveToolbarKey( toolBarItem );
|
||||||
|
} }
|
||||||
|
MediaUploadComponent={ MediaUploadComponent }
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -87,7 +147,7 @@ export const ImageGallery: React.FC< ImageGalleryProps > = ( {
|
||||||
>
|
>
|
||||||
<ImageGalleryWrapper
|
<ImageGalleryWrapper
|
||||||
allowDragging={ allowDragging }
|
allowDragging={ allowDragging }
|
||||||
updateOrderedChildren={ updateOrderedChildren }
|
updateOrderedChildren={ onOrderChange }
|
||||||
onDragStart={ ( event ) => {
|
onDragStart={ ( event ) => {
|
||||||
setIsDragging( true );
|
setIsDragging( true );
|
||||||
onDragStart( event );
|
onDragStart( event );
|
||||||
|
@ -98,114 +158,7 @@ export const ImageGallery: React.FC< ImageGalleryProps > = ( {
|
||||||
} }
|
} }
|
||||||
onDragOver={ onDragOver }
|
onDragOver={ onDragOver }
|
||||||
>
|
>
|
||||||
{ orderedChildren.map( ( child, childIndex ) => {
|
{ childElements.map( cloneChild ) }
|
||||||
const isToolbarVisible = child.key === activeToolbarKey;
|
|
||||||
|
|
||||||
return cloneElement(
|
|
||||||
child,
|
|
||||||
{
|
|
||||||
isDraggable: allowDragging && ! child.props.isCover,
|
|
||||||
className: classnames( {
|
|
||||||
'is-toolbar-visible': isToolbarVisible,
|
|
||||||
} ),
|
|
||||||
onClick: () => {
|
|
||||||
setActiveToolbarKey(
|
|
||||||
isToolbarVisible
|
|
||||||
? null
|
|
||||||
: ( child.key as string )
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onBlur: (
|
|
||||||
event: React.FocusEvent< HTMLDivElement >
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
isDragging ||
|
|
||||||
event.currentTarget.contains(
|
|
||||||
event.relatedTarget
|
|
||||||
) ||
|
|
||||||
( event.relatedTarget &&
|
|
||||||
(
|
|
||||||
event.relatedTarget as Element
|
|
||||||
).closest(
|
|
||||||
'.media-modal, .components-modal__frame'
|
|
||||||
) ) ||
|
|
||||||
( event.relatedTarget &&
|
|
||||||
// Check if not a button within the toolbar is clicked, to prevent hiding the toolbar.
|
|
||||||
(
|
|
||||||
event.relatedTarget as Element
|
|
||||||
).closest(
|
|
||||||
'.woocommerce-image-gallery__toolbar'
|
|
||||||
) ) ||
|
|
||||||
( event.relatedTarget &&
|
|
||||||
// Prevent toolbar from hiding if the dropdown is clicked within the toolbar.
|
|
||||||
(
|
|
||||||
event.relatedTarget as Element
|
|
||||||
).closest(
|
|
||||||
'.woocommerce-image-gallery__toolbar-dropdown-popover'
|
|
||||||
) )
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setActiveToolbarKey( null );
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isToolbarVisible ? (
|
|
||||||
<ImageGalleryToolbar
|
|
||||||
value={ child.props.id }
|
|
||||||
allowDragging={ allowDragging }
|
|
||||||
childIndex={ childIndex }
|
|
||||||
lastChild={
|
|
||||||
childIndex === orderedChildren.length - 1
|
|
||||||
}
|
|
||||||
moveItem={ (
|
|
||||||
fromIndex: number,
|
|
||||||
toIndex: number
|
|
||||||
) => {
|
|
||||||
updateOrderedChildren(
|
|
||||||
moveIndex< ImageGalleryChild >(
|
|
||||||
fromIndex,
|
|
||||||
toIndex,
|
|
||||||
orderedChildren
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} }
|
|
||||||
removeItem={ ( removeIndex: number ) => {
|
|
||||||
onRemove( {
|
|
||||||
removeIndex,
|
|
||||||
removedItem:
|
|
||||||
orderedChildren[ removeIndex ],
|
|
||||||
} );
|
|
||||||
updateOrderedChildren(
|
|
||||||
removeItem(
|
|
||||||
orderedChildren,
|
|
||||||
removeIndex
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} }
|
|
||||||
replaceItem={ (
|
|
||||||
replaceIndex: number,
|
|
||||||
media: { id: number } & MediaItem
|
|
||||||
) => {
|
|
||||||
onReplace( { replaceIndex, media } );
|
|
||||||
setOrderedChildren(
|
|
||||||
replaceItem< {
|
|
||||||
src: string;
|
|
||||||
alt: string;
|
|
||||||
} >( orderedChildren, replaceIndex, {
|
|
||||||
src: media.url as string,
|
|
||||||
alt: media.alt as string,
|
|
||||||
} )
|
|
||||||
);
|
|
||||||
} }
|
|
||||||
setToolBarItem={ ( toolBarItem ) => {
|
|
||||||
onSelectAsCover( activeToolbarKey );
|
|
||||||
setActiveToolbarKey( toolBarItem );
|
|
||||||
} }
|
|
||||||
MediaUploadComponent={ MediaUploadComponent }
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
);
|
|
||||||
} ) }
|
|
||||||
</ImageGalleryWrapper>
|
</ImageGalleryWrapper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: fix
|
||||||
|
|
||||||
|
Fix the image gallery removal conflict now that the image gallery is stateless
|
|
@ -162,10 +162,12 @@ export function ImageBlockEdit( {
|
||||||
} ) {
|
} ) {
|
||||||
recordEvent( 'product_images_replace_image_button_click' );
|
recordEvent( 'product_images_replace_image_button_click' );
|
||||||
|
|
||||||
if (
|
if ( Array.isArray( propertyValue ) ) {
|
||||||
Array.isArray( propertyValue ) &&
|
// Ignore the media if it is replaced by itseft.
|
||||||
! propertyValue.some( ( img ) => media.id === img.id )
|
if ( propertyValue.some( ( img ) => media.id === img.id ) ) {
|
||||||
) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const image = mapUploadImageToImage( media );
|
const image = mapUploadImageToImage( media );
|
||||||
if ( image ) {
|
if ( image ) {
|
||||||
const newImages = [ ...propertyValue ];
|
const newImages = [ ...propertyValue ];
|
||||||
|
@ -182,7 +184,7 @@ export function ImageBlockEdit( {
|
||||||
|
|
||||||
if ( Array.isArray( propertyValue ) ) {
|
if ( Array.isArray( propertyValue ) ) {
|
||||||
const remainingImages = propertyValue.filter(
|
const remainingImages = propertyValue.filter(
|
||||||
( image ) => image.id === removedItem.props.id
|
( image ) => String( image.id ) !== removedItem.props.id
|
||||||
);
|
);
|
||||||
setPropertyValue( remainingImages );
|
setPropertyValue( remainingImages );
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue