Migrating product editor images section to slot-fill (#36461)
This commit is contained in:
parent
7e17a96914
commit
687dd6fdfe
|
@ -0,0 +1,7 @@
|
||||||
|
export const PRODUCT_DETAILS_SLUG = 'product-details';
|
||||||
|
|
||||||
|
export const DETAILS_SECTION_ID = 'general/details';
|
||||||
|
export const IMAGES_SECTION_ID = 'general/images';
|
||||||
|
|
||||||
|
export const TAB_GENERAL_ID = 'tab/general';
|
||||||
|
export const PLUGIN_ID = 'woocommerce';
|
|
@ -1,2 +0,0 @@
|
||||||
export const PRODUCT_DETAILS_SLUG = 'product-details';
|
|
||||||
export const DETAILS_SECTION_ID = 'general/details';
|
|
|
@ -16,7 +16,7 @@ import { recordEvent } from '@woocommerce/tracks';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getCheckboxTracks } from '../../sections/utils';
|
import { getCheckboxTracks } from '../../sections/utils';
|
||||||
import { PRODUCT_DETAILS_SLUG } from './index';
|
import { PRODUCT_DETAILS_SLUG } from '../constants';
|
||||||
|
|
||||||
export const DetailsFeatureField = () => {
|
export const DetailsFeatureField = () => {
|
||||||
const { getCheckboxControlProps } = useFormContext< Product >();
|
const { getCheckboxControlProps } = useFormContext< Product >();
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { useState } from '@wordpress/element';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { EditProductLinkModal } from '../../shared/edit-product-link-modal';
|
import { EditProductLinkModal } from '../../shared/edit-product-link-modal';
|
||||||
import { PRODUCT_DETAILS_SLUG } from './index';
|
import { PRODUCT_DETAILS_SLUG } from '../constants';
|
||||||
|
|
||||||
export const DetailsNameField = ( {} ) => {
|
export const DetailsNameField = ( {} ) => {
|
||||||
const [ showProductLinkEditModal, setShowProductLinkEditModal ] =
|
const [ showProductLinkEditModal, setShowProductLinkEditModal ] =
|
||||||
|
|
|
@ -18,20 +18,22 @@ import {
|
||||||
DetailsFeatureField,
|
DetailsFeatureField,
|
||||||
DetailsSummaryField,
|
DetailsSummaryField,
|
||||||
DetailsDescriptionField,
|
DetailsDescriptionField,
|
||||||
DETAILS_SECTION_ID,
|
|
||||||
} from './index';
|
} from './index';
|
||||||
|
|
||||||
|
import { DETAILS_SECTION_ID, PLUGIN_ID, TAB_GENERAL_ID } from '../constants';
|
||||||
|
|
||||||
import './product-details-section.scss';
|
import './product-details-section.scss';
|
||||||
|
|
||||||
const DetailsSection = () => (
|
const DetailsSection = () => (
|
||||||
<>
|
<>
|
||||||
<WooProductSectionItem
|
<WooProductSectionItem
|
||||||
id={ DETAILS_SECTION_ID }
|
id={ DETAILS_SECTION_ID }
|
||||||
location="tab/general"
|
location={ TAB_GENERAL_ID }
|
||||||
pluginId="core"
|
pluginId={ PLUGIN_ID }
|
||||||
order={ 1 }
|
order={ 1 }
|
||||||
>
|
>
|
||||||
<ProductFieldSection
|
<ProductFieldSection
|
||||||
id="general/details"
|
id={ DETAILS_SECTION_ID }
|
||||||
title={ __( 'Product details', 'woocommerce' ) }
|
title={ __( 'Product details', 'woocommerce' ) }
|
||||||
description={ __(
|
description={ __(
|
||||||
'This info will be displayed on the product page, category pages, social media, and search results.',
|
'This info will be displayed on the product page, category pages, social media, and search results.',
|
||||||
|
@ -42,7 +44,7 @@ const DetailsSection = () => (
|
||||||
<WooProductFieldItem
|
<WooProductFieldItem
|
||||||
id="details/name"
|
id="details/name"
|
||||||
section={ DETAILS_SECTION_ID }
|
section={ DETAILS_SECTION_ID }
|
||||||
pluginId="core"
|
pluginId={ PLUGIN_ID }
|
||||||
order={ 1 }
|
order={ 1 }
|
||||||
>
|
>
|
||||||
<DetailsNameField />
|
<DetailsNameField />
|
||||||
|
@ -50,7 +52,7 @@ const DetailsSection = () => (
|
||||||
<WooProductFieldItem
|
<WooProductFieldItem
|
||||||
id="details/categories"
|
id="details/categories"
|
||||||
section={ DETAILS_SECTION_ID }
|
section={ DETAILS_SECTION_ID }
|
||||||
pluginId="core"
|
pluginId={ PLUGIN_ID }
|
||||||
order={ 3 }
|
order={ 3 }
|
||||||
>
|
>
|
||||||
<DetailsCategoriesField />
|
<DetailsCategoriesField />
|
||||||
|
@ -58,7 +60,7 @@ const DetailsSection = () => (
|
||||||
<WooProductFieldItem
|
<WooProductFieldItem
|
||||||
id="details/feature"
|
id="details/feature"
|
||||||
section={ DETAILS_SECTION_ID }
|
section={ DETAILS_SECTION_ID }
|
||||||
pluginId="core"
|
pluginId={ PLUGIN_ID }
|
||||||
order={ 5 }
|
order={ 5 }
|
||||||
>
|
>
|
||||||
<DetailsFeatureField />
|
<DetailsFeatureField />
|
||||||
|
@ -66,7 +68,7 @@ const DetailsSection = () => (
|
||||||
<WooProductFieldItem
|
<WooProductFieldItem
|
||||||
id="details/summary"
|
id="details/summary"
|
||||||
section={ DETAILS_SECTION_ID }
|
section={ DETAILS_SECTION_ID }
|
||||||
pluginId="core"
|
pluginId={ PLUGIN_ID }
|
||||||
order={ 7 }
|
order={ 7 }
|
||||||
>
|
>
|
||||||
<DetailsSummaryField />
|
<DetailsSummaryField />
|
||||||
|
@ -74,7 +76,7 @@ const DetailsSection = () => (
|
||||||
<WooProductFieldItem
|
<WooProductFieldItem
|
||||||
id="details/description"
|
id="details/description"
|
||||||
section={ DETAILS_SECTION_ID }
|
section={ DETAILS_SECTION_ID }
|
||||||
pluginId="core"
|
pluginId={ PLUGIN_ID }
|
||||||
order={ 9 }
|
order={ 9 }
|
||||||
>
|
>
|
||||||
<DetailsDescriptionField />
|
<DetailsDescriptionField />
|
||||||
|
|
|
@ -3,4 +3,3 @@ export * from './details-field-categories';
|
||||||
export * from './details-field-feature';
|
export * from './details-field-feature';
|
||||||
export * from './details-field-summary';
|
export * from './details-field-summary';
|
||||||
export * from './details-field-description';
|
export * from './details-field-description';
|
||||||
export * from './constants';
|
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import {
|
||||||
|
useFormContext,
|
||||||
|
MediaUploader,
|
||||||
|
ImageGallery,
|
||||||
|
ImageGalleryItem,
|
||||||
|
} from '@woocommerce/components';
|
||||||
|
import { CardBody, DropZone } from '@wordpress/components';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
import { useState } from '@wordpress/element';
|
||||||
|
import { Product } from '@woocommerce/data';
|
||||||
|
import { Icon, trash } from '@wordpress/icons';
|
||||||
|
import { MediaItem } from '@wordpress/media-utils';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import DragAndDrop from '../../images/drag-and-drop.svg';
|
||||||
|
|
||||||
|
type Image = MediaItem & {
|
||||||
|
src: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ImagesGalleryField = () => {
|
||||||
|
const { getInputProps, setValue } = useFormContext< Product >();
|
||||||
|
const images = ( getInputProps( 'images' ).value as Image[] ) || [];
|
||||||
|
const [ isRemovingZoneVisible, setIsRemovingZoneVisible ] =
|
||||||
|
useState< boolean >( false );
|
||||||
|
const [ isRemoving, setIsRemoving ] = useState< boolean >( false );
|
||||||
|
const [ draggedImageId, setDraggedImageId ] = useState< number | null >(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
const toggleRemoveZone = () => {
|
||||||
|
setIsRemovingZoneVisible( ! isRemovingZoneVisible );
|
||||||
|
};
|
||||||
|
|
||||||
|
const orderImages = ( newOrder: JSX.Element[] ) => {
|
||||||
|
const orderedImages = newOrder.map( ( image ) => {
|
||||||
|
return images.find(
|
||||||
|
( file ) => file.id === parseInt( image?.props?.id, 10 )
|
||||||
|
);
|
||||||
|
} );
|
||||||
|
recordEvent( 'product_images_change_image_order_via_image_gallery' );
|
||||||
|
setValue( 'images', orderedImages );
|
||||||
|
};
|
||||||
|
const onFileUpload = ( files: MediaItem[] ) => {
|
||||||
|
if ( files[ 0 ].id ) {
|
||||||
|
recordEvent( 'product_images_add_via_file_upload_area' );
|
||||||
|
setValue( 'images', [ ...images, ...files ] );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={ classnames( 'woocommerce-product-form__images', {
|
||||||
|
'has-images': images.length > 0,
|
||||||
|
} ) }
|
||||||
|
>
|
||||||
|
<ImageGallery
|
||||||
|
onDragStart={ ( event ) => {
|
||||||
|
const { id: imageId, dataset } =
|
||||||
|
event.target as HTMLElement;
|
||||||
|
if ( imageId ) {
|
||||||
|
setDraggedImageId( parseInt( imageId, 10 ) );
|
||||||
|
} else {
|
||||||
|
const index = dataset?.index;
|
||||||
|
if ( index ) {
|
||||||
|
setDraggedImageId(
|
||||||
|
images[ parseInt( index, 10 ) ]?.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggleRemoveZone();
|
||||||
|
} }
|
||||||
|
onDragEnd={ () => {
|
||||||
|
if ( isRemoving && draggedImageId ) {
|
||||||
|
recordEvent(
|
||||||
|
'product_images_remove_image_button_click'
|
||||||
|
);
|
||||||
|
setValue(
|
||||||
|
'images',
|
||||||
|
images.filter(
|
||||||
|
( img ) => img.id !== draggedImageId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setIsRemoving( false );
|
||||||
|
setDraggedImageId( null );
|
||||||
|
}
|
||||||
|
toggleRemoveZone();
|
||||||
|
} }
|
||||||
|
onOrderChange={ orderImages }
|
||||||
|
onReplace={ ( { replaceIndex, media } ) => {
|
||||||
|
if (
|
||||||
|
images.find( ( img ) => media.id === img.id ) ===
|
||||||
|
undefined
|
||||||
|
) {
|
||||||
|
images[ replaceIndex ] = media as Image;
|
||||||
|
recordEvent(
|
||||||
|
'product_images_replace_image_button_click'
|
||||||
|
);
|
||||||
|
setValue( 'images', images );
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
onSelectAsCover={ () =>
|
||||||
|
recordEvent(
|
||||||
|
'product_images_select_image_as_cover_button_click'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{ images.map( ( image ) => (
|
||||||
|
<ImageGalleryItem
|
||||||
|
key={ image.id || image.url }
|
||||||
|
alt={ image.alt }
|
||||||
|
src={ image.url || image.src }
|
||||||
|
id={ `${ image.id }` }
|
||||||
|
/>
|
||||||
|
) ) }
|
||||||
|
</ImageGallery>
|
||||||
|
<div className="woocommerce-product-form__image-drop-zone">
|
||||||
|
{ isRemovingZoneVisible ? (
|
||||||
|
<CardBody>
|
||||||
|
<div className="woocommerce-product-form__remove-image-drop-zone">
|
||||||
|
<span>
|
||||||
|
<Icon
|
||||||
|
icon={ trash }
|
||||||
|
size={ 20 }
|
||||||
|
className="icon-control"
|
||||||
|
/>
|
||||||
|
{ __( 'Drop here to remove', 'woocommerce' ) }
|
||||||
|
</span>
|
||||||
|
<DropZone
|
||||||
|
onHTMLDrop={ () => setIsRemoving( true ) }
|
||||||
|
onDrop={ () => setIsRemoving( true ) }
|
||||||
|
label={ __(
|
||||||
|
'Drop here to remove',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardBody>
|
||||||
|
) : (
|
||||||
|
<CardBody>
|
||||||
|
<MediaUploader
|
||||||
|
multipleSelect={ true }
|
||||||
|
onError={ () => null }
|
||||||
|
onFileUploadChange={ onFileUpload }
|
||||||
|
onSelect={ ( files ) => {
|
||||||
|
const newImages = files.filter(
|
||||||
|
( img: Image ) =>
|
||||||
|
! images.find(
|
||||||
|
( image ) => image.id === img.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ( newImages.length > 0 ) {
|
||||||
|
recordEvent(
|
||||||
|
'product_images_add_via_media_library'
|
||||||
|
);
|
||||||
|
setValue( 'images', [
|
||||||
|
...images,
|
||||||
|
...newImages,
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
onUpload={ ( files ) => {
|
||||||
|
if ( files[ 0 ].id ) {
|
||||||
|
recordEvent(
|
||||||
|
'product_images_add_via_drag_and_drop_upload'
|
||||||
|
);
|
||||||
|
setValue( 'images', [
|
||||||
|
...images,
|
||||||
|
...files,
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<img
|
||||||
|
src={ DragAndDrop }
|
||||||
|
alt={ __( 'Completed', 'woocommerce' ) }
|
||||||
|
className="woocommerce-product-form__drag-and-drop-image"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{ __(
|
||||||
|
'Drag images here or click to upload',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</CardBody>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,74 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import {
|
||||||
|
__experimentalWooProductSectionItem as WooProductSectionItem,
|
||||||
|
__experimentalWooProductFieldItem as WooProductFieldItem,
|
||||||
|
__experimentalProductFieldSection as ProductFieldSection,
|
||||||
|
Link,
|
||||||
|
} from '@woocommerce/components';
|
||||||
|
import { registerPlugin } from '@wordpress/plugins';
|
||||||
|
import { recordEvent } from '@woocommerce/tracks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import { ImagesGalleryField } from './index';
|
||||||
|
import { IMAGES_SECTION_ID, TAB_GENERAL_ID, PLUGIN_ID } from '../constants';
|
||||||
|
|
||||||
|
import './images-section.scss';
|
||||||
|
|
||||||
|
const ImagesSection = () => (
|
||||||
|
<>
|
||||||
|
<WooProductSectionItem
|
||||||
|
id={ IMAGES_SECTION_ID }
|
||||||
|
location={ TAB_GENERAL_ID }
|
||||||
|
pluginId={ PLUGIN_ID }
|
||||||
|
order={ 3 }
|
||||||
|
>
|
||||||
|
<ProductFieldSection
|
||||||
|
id={ IMAGES_SECTION_ID }
|
||||||
|
title={ __( 'Images', 'woocommerce' ) }
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
{ __(
|
||||||
|
'For best results, use JPEG files that are 1000 by 1000 pixels or larger.',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
</span>
|
||||||
|
<Link
|
||||||
|
className="woocommerce-form-section__header-link"
|
||||||
|
href="https://woocommerce.com/posts/fast-high-quality-product-photos/"
|
||||||
|
target="_blank"
|
||||||
|
type="external"
|
||||||
|
onClick={ () => {
|
||||||
|
recordEvent( 'prepare_images_help' );
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
{ __(
|
||||||
|
'How should I prepare images?',
|
||||||
|
'woocommerce'
|
||||||
|
) }
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</WooProductSectionItem>
|
||||||
|
<WooProductFieldItem
|
||||||
|
id="images/gallery"
|
||||||
|
section={ IMAGES_SECTION_ID }
|
||||||
|
pluginId={ PLUGIN_ID }
|
||||||
|
order={ 1 }
|
||||||
|
>
|
||||||
|
<ImagesGalleryField />
|
||||||
|
</WooProductFieldItem>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
registerPlugin( 'wc-admin-product-editor-images-section', {
|
||||||
|
// @ts-expect-error 'scope' does exist. @types/wordpress__plugins is outdated.
|
||||||
|
scope: 'woocommerce-product-editor',
|
||||||
|
render: () => <ImagesSection />,
|
||||||
|
} );
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './images-field-gallery';
|
|
@ -4,3 +4,4 @@
|
||||||
import './product-form-fills';
|
import './product-form-fills';
|
||||||
|
|
||||||
export * from './details-section/details-section-fills';
|
export * from './details-section/details-section-fills';
|
||||||
|
export * from './images-section/images-section-fills';
|
||||||
|
|
|
@ -20,12 +20,12 @@ import { ProductInventorySection } from './sections/product-inventory-section';
|
||||||
import { PricingSection } from './sections/pricing-section';
|
import { PricingSection } from './sections/pricing-section';
|
||||||
import { ProductShippingSection } from './sections/product-shipping-section';
|
import { ProductShippingSection } from './sections/product-shipping-section';
|
||||||
import { ProductVariationsSection } from './sections/product-variations-section';
|
import { ProductVariationsSection } from './sections/product-variations-section';
|
||||||
import { ImagesSection } from './sections/images-section';
|
|
||||||
import { validate } from './product-validation';
|
import { validate } from './product-validation';
|
||||||
import { AttributesSection } from './sections/attributes-section';
|
import { AttributesSection } from './sections/attributes-section';
|
||||||
import { OptionsSection } from './sections/options-section';
|
import { OptionsSection } from './sections/options-section';
|
||||||
import { ProductFormFooter } from './layout/product-form-footer';
|
import { ProductFormFooter } from './layout/product-form-footer';
|
||||||
import { ProductFormTab } from './product-form-tab';
|
import { ProductFormTab } from './product-form-tab';
|
||||||
|
import { TAB_GENERAL_ID } from './fills/constants';
|
||||||
|
|
||||||
export const ProductForm: React.FC< {
|
export const ProductForm: React.FC< {
|
||||||
product?: PartialProduct;
|
product?: PartialProduct;
|
||||||
|
@ -50,8 +50,9 @@ export const ProductForm: React.FC< {
|
||||||
<ProductFormHeader />
|
<ProductFormHeader />
|
||||||
<ProductFormLayout>
|
<ProductFormLayout>
|
||||||
<ProductFormTab name="general" title="General">
|
<ProductFormTab name="general" title="General">
|
||||||
<WooProductSectionItem.Slot location="tab/general" />
|
<WooProductSectionItem.Slot
|
||||||
<ImagesSection />
|
location={ TAB_GENERAL_ID }
|
||||||
|
/>
|
||||||
<AttributesSection />
|
<AttributesSection />
|
||||||
</ProductFormTab>
|
</ProductFormTab>
|
||||||
<ProductFormTab
|
<ProductFormTab
|
||||||
|
|
|
@ -1,241 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
import {
|
|
||||||
Link,
|
|
||||||
useFormContext,
|
|
||||||
MediaUploader,
|
|
||||||
ImageGallery,
|
|
||||||
ImageGalleryItem,
|
|
||||||
} from '@woocommerce/components';
|
|
||||||
import { Card, CardBody, DropZone } from '@wordpress/components';
|
|
||||||
import { recordEvent } from '@woocommerce/tracks';
|
|
||||||
import { useState } from '@wordpress/element';
|
|
||||||
import { Product } from '@woocommerce/data';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { Icon, trash } from '@wordpress/icons';
|
|
||||||
import { MediaItem } from '@wordpress/media-utils';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { ProductSectionLayout } from '../layout/product-section-layout';
|
|
||||||
import DragAndDrop from '../images/drag-and-drop.svg';
|
|
||||||
import './images-section.scss';
|
|
||||||
|
|
||||||
type Image = MediaItem & {
|
|
||||||
src: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ImagesSection: React.FC = () => {
|
|
||||||
const { getInputProps, setValue } = useFormContext< Product >();
|
|
||||||
const images = ( getInputProps( 'images' ).value as Image[] ) || [];
|
|
||||||
const [ isRemovingZoneVisible, setIsRemovingZoneVisible ] =
|
|
||||||
useState< boolean >( false );
|
|
||||||
const [ isRemoving, setIsRemoving ] = useState< boolean >( false );
|
|
||||||
const [ draggedImageId, setDraggedImageId ] = useState< number | null >(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
const toggleRemoveZone = () => {
|
|
||||||
setIsRemovingZoneVisible( ! isRemovingZoneVisible );
|
|
||||||
};
|
|
||||||
|
|
||||||
const orderImages = ( newOrder: JSX.Element[] ) => {
|
|
||||||
const orderedImages = newOrder.map( ( image ) => {
|
|
||||||
return images.find(
|
|
||||||
( file ) => file.id === parseInt( image?.props?.id, 10 )
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
recordEvent( 'product_images_change_image_order_via_image_gallery' );
|
|
||||||
setValue( 'images', orderedImages );
|
|
||||||
};
|
|
||||||
const onFileUpload = ( files: MediaItem[] ) => {
|
|
||||||
if ( files[ 0 ].id ) {
|
|
||||||
recordEvent( 'product_images_add_via_file_upload_area' );
|
|
||||||
setValue( 'images', [ ...images, ...files ] );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ProductSectionLayout
|
|
||||||
title={ __( 'Images', 'woocommerce' ) }
|
|
||||||
description={
|
|
||||||
<>
|
|
||||||
<span>
|
|
||||||
{ __(
|
|
||||||
'For best results, use JPEG files that are 1000 by 1000 pixels or larger.',
|
|
||||||
'woocommerce'
|
|
||||||
) }
|
|
||||||
</span>
|
|
||||||
<Link
|
|
||||||
className="woocommerce-form-section__header-link"
|
|
||||||
href="https://woocommerce.com/posts/fast-high-quality-product-photos/"
|
|
||||||
target="_blank"
|
|
||||||
type="external"
|
|
||||||
onClick={ () => {
|
|
||||||
recordEvent( 'prepare_images_help' );
|
|
||||||
} }
|
|
||||||
>
|
|
||||||
{ __( 'How should I prepare images?', 'woocommerce' ) }
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
className={ classnames( 'woocommerce-product-form__images', {
|
|
||||||
'has-images': images.length > 0,
|
|
||||||
} ) }
|
|
||||||
>
|
|
||||||
<CardBody>
|
|
||||||
<ImageGallery
|
|
||||||
onDragStart={ ( event ) => {
|
|
||||||
const { id: imageId, dataset } =
|
|
||||||
event.target as HTMLElement;
|
|
||||||
if ( imageId ) {
|
|
||||||
setDraggedImageId( parseInt( imageId, 10 ) );
|
|
||||||
} else {
|
|
||||||
const index = dataset?.index;
|
|
||||||
if ( index ) {
|
|
||||||
setDraggedImageId(
|
|
||||||
images[ parseInt( index, 10 ) ]?.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toggleRemoveZone();
|
|
||||||
} }
|
|
||||||
onDragEnd={ () => {
|
|
||||||
if ( isRemoving && draggedImageId ) {
|
|
||||||
recordEvent(
|
|
||||||
'product_images_remove_image_button_click'
|
|
||||||
);
|
|
||||||
setValue(
|
|
||||||
'images',
|
|
||||||
images.filter(
|
|
||||||
( img ) => img.id !== draggedImageId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
setIsRemoving( false );
|
|
||||||
setDraggedImageId( null );
|
|
||||||
}
|
|
||||||
toggleRemoveZone();
|
|
||||||
} }
|
|
||||||
onOrderChange={ orderImages }
|
|
||||||
onReplace={ ( { replaceIndex, media } ) => {
|
|
||||||
if (
|
|
||||||
images.find(
|
|
||||||
( img ) => media.id === img.id
|
|
||||||
) === undefined
|
|
||||||
) {
|
|
||||||
images[ replaceIndex ] = media as Image;
|
|
||||||
recordEvent(
|
|
||||||
'product_images_replace_image_button_click'
|
|
||||||
);
|
|
||||||
setValue( 'images', images );
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
onSelectAsCover={ () =>
|
|
||||||
recordEvent(
|
|
||||||
'product_images_select_image_as_cover_button_click'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{ images.map( ( image ) => (
|
|
||||||
<ImageGalleryItem
|
|
||||||
key={ image.id || image.url }
|
|
||||||
alt={ image.alt }
|
|
||||||
src={ image.url || image.src }
|
|
||||||
id={ `${ image.id }` }
|
|
||||||
/>
|
|
||||||
) ) }
|
|
||||||
</ImageGallery>
|
|
||||||
<div className="woocommerce-product-form__image-drop-zone">
|
|
||||||
{ isRemovingZoneVisible ? (
|
|
||||||
<CardBody>
|
|
||||||
<div className="woocommerce-product-form__remove-image-drop-zone">
|
|
||||||
<span>
|
|
||||||
<Icon
|
|
||||||
icon={ trash }
|
|
||||||
size={ 20 }
|
|
||||||
className="icon-control"
|
|
||||||
/>
|
|
||||||
{ __(
|
|
||||||
'Drop here to remove',
|
|
||||||
'woocommerce'
|
|
||||||
) }
|
|
||||||
</span>
|
|
||||||
<DropZone
|
|
||||||
onHTMLDrop={ () =>
|
|
||||||
setIsRemoving( true )
|
|
||||||
}
|
|
||||||
onDrop={ () => setIsRemoving( true ) }
|
|
||||||
label={ __(
|
|
||||||
'Drop here to remove',
|
|
||||||
'woocommerce'
|
|
||||||
) }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
) : (
|
|
||||||
<CardBody>
|
|
||||||
<MediaUploader
|
|
||||||
multipleSelect={ true }
|
|
||||||
onError={ () => null }
|
|
||||||
onFileUploadChange={ onFileUpload }
|
|
||||||
onSelect={ ( files ) => {
|
|
||||||
const newImages = files.filter(
|
|
||||||
( img: Image ) =>
|
|
||||||
! images.find(
|
|
||||||
( image ) =>
|
|
||||||
image.id === img.id
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if ( newImages.length > 0 ) {
|
|
||||||
recordEvent(
|
|
||||||
'product_images_add_via_media_library'
|
|
||||||
);
|
|
||||||
setValue( 'images', [
|
|
||||||
...images,
|
|
||||||
...newImages,
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
onUpload={ ( files ) => {
|
|
||||||
if ( files[ 0 ].id ) {
|
|
||||||
recordEvent(
|
|
||||||
'product_images_add_via_drag_and_drop_upload'
|
|
||||||
);
|
|
||||||
setValue( 'images', [
|
|
||||||
...images,
|
|
||||||
...files,
|
|
||||||
] );
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
label={
|
|
||||||
<>
|
|
||||||
<img
|
|
||||||
src={ DragAndDrop }
|
|
||||||
alt={ __(
|
|
||||||
'Completed',
|
|
||||||
'woocommerce'
|
|
||||||
) }
|
|
||||||
className="woocommerce-product-form__drag-and-drop-image"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
{ __(
|
|
||||||
'Drag images here or click to upload',
|
|
||||||
'woocommerce'
|
|
||||||
) }
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CardBody>
|
|
||||||
) }
|
|
||||||
</div>
|
|
||||||
</CardBody>
|
|
||||||
</Card>
|
|
||||||
</ProductSectionLayout>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: minor
|
||||||
|
Type: add
|
||||||
|
|
||||||
|
Using slotfill to insert images section in product editor.
|
Loading…
Reference in New Issue