2022-05-19 16:16:46 +00:00
|
|
|
/**
|
|
|
|
* External dependencies
|
|
|
|
*/
|
2022-05-30 14:38:52 +00:00
|
|
|
import { ComponentType, Dispatch, SetStateAction } from 'react';
|
2022-05-19 16:16:46 +00:00
|
|
|
import { __ } from '@wordpress/i18n';
|
|
|
|
import {
|
|
|
|
AlignmentToolbar,
|
|
|
|
BlockControls as BlockControlsWrapper,
|
|
|
|
MediaReplaceFlow,
|
|
|
|
} from '@wordpress/block-editor';
|
2022-05-30 14:38:52 +00:00
|
|
|
import type { BlockAlignment } from '@wordpress/blocks';
|
2022-05-19 16:16:46 +00:00
|
|
|
import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
|
|
|
|
import { crop } from '@wordpress/icons';
|
2022-05-30 14:38:52 +00:00
|
|
|
import { WP_REST_API_Category } from 'wp-types';
|
|
|
|
import { ProductResponseItem } from '@woocommerce/types';
|
2022-05-19 16:16:46 +00:00
|
|
|
import TextToolbarButton from '@woocommerce/editor-components/text-toolbar-button';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal dependencies
|
|
|
|
*/
|
|
|
|
import { useBackgroundImage } from './use-background-image';
|
2022-05-30 14:38:52 +00:00
|
|
|
import { EditorBlock, GenericBlockUIConfig } from './types';
|
|
|
|
|
|
|
|
type Media = { id: number; url: string };
|
|
|
|
|
|
|
|
interface WithBlockControlsRequiredProps< T > {
|
|
|
|
attributes: BlockControlRequiredAttributes &
|
|
|
|
EditorBlock< T >[ 'attributes' ];
|
|
|
|
setAttributes: ( attrs: Partial< BlockControlRequiredAttributes > ) => void;
|
|
|
|
useEditingImage: [ boolean, Dispatch< SetStateAction< boolean > > ];
|
|
|
|
}
|
|
|
|
|
|
|
|
interface WithBlockControlsCategoryProps< T >
|
|
|
|
extends WithBlockControlsRequiredProps< T > {
|
|
|
|
category: WP_REST_API_Category;
|
|
|
|
product: never;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface WithBlockControlsProductProps< T >
|
|
|
|
extends WithBlockControlsRequiredProps< T > {
|
|
|
|
category: never;
|
|
|
|
product: ProductResponseItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
type WithBlockControlsProps< T extends EditorBlock< T > > =
|
|
|
|
| ( T & WithBlockControlsCategoryProps< T > )
|
|
|
|
| ( T & WithBlockControlsProductProps< T > );
|
|
|
|
|
|
|
|
type BlockControlRequiredAttributes = {
|
|
|
|
contentAlign: BlockAlignment;
|
|
|
|
editMode: boolean;
|
|
|
|
mediaId: number;
|
|
|
|
mediaSrc: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
interface BlockControlsProps {
|
|
|
|
backgroundImageId: number;
|
|
|
|
backgroundImageSrc: string;
|
|
|
|
contentAlign: BlockAlignment;
|
|
|
|
cropLabel: string;
|
|
|
|
editLabel: string;
|
|
|
|
editMode: boolean;
|
|
|
|
isEditingImage: boolean;
|
|
|
|
mediaSrc: string;
|
|
|
|
setAttributes: ( attrs: Partial< BlockControlRequiredAttributes > ) => void;
|
|
|
|
setIsEditingImage: ( value: boolean ) => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface BlockControlsConfiguration extends GenericBlockUIConfig {
|
|
|
|
cropLabel: string;
|
|
|
|
editLabel: string;
|
|
|
|
}
|
2022-05-19 16:16:46 +00:00
|
|
|
|
|
|
|
export const BlockControls = ( {
|
|
|
|
backgroundImageId,
|
|
|
|
backgroundImageSrc,
|
|
|
|
contentAlign,
|
|
|
|
cropLabel,
|
|
|
|
editLabel,
|
|
|
|
editMode,
|
|
|
|
isEditingImage,
|
|
|
|
mediaSrc,
|
|
|
|
setAttributes,
|
|
|
|
setIsEditingImage,
|
2022-05-30 14:38:52 +00:00
|
|
|
}: BlockControlsProps ) => {
|
2022-05-19 16:16:46 +00:00
|
|
|
return (
|
|
|
|
<BlockControlsWrapper>
|
|
|
|
<AlignmentToolbar
|
|
|
|
value={ contentAlign }
|
2022-05-30 14:38:52 +00:00
|
|
|
onChange={ ( nextAlign: BlockAlignment ) => {
|
2022-05-19 16:16:46 +00:00
|
|
|
setAttributes( { contentAlign: nextAlign } );
|
|
|
|
} }
|
|
|
|
/>
|
|
|
|
<ToolbarGroup>
|
|
|
|
{ backgroundImageSrc && ! isEditingImage && (
|
|
|
|
<ToolbarButton
|
|
|
|
onClick={ () => setIsEditingImage( true ) }
|
|
|
|
icon={ crop }
|
|
|
|
label={ cropLabel }
|
|
|
|
/>
|
|
|
|
) }
|
|
|
|
<MediaReplaceFlow
|
|
|
|
mediaId={ backgroundImageId }
|
|
|
|
mediaURL={ mediaSrc }
|
|
|
|
accept="image/*"
|
2022-05-30 14:38:52 +00:00
|
|
|
onSelect={ ( media: Media ) => {
|
2022-05-19 16:16:46 +00:00
|
|
|
setAttributes( {
|
|
|
|
mediaId: media.id,
|
|
|
|
mediaSrc: media.url,
|
|
|
|
} );
|
|
|
|
} }
|
|
|
|
allowedTypes={ [ 'image' ] }
|
|
|
|
/>
|
|
|
|
{ backgroundImageId && mediaSrc ? (
|
|
|
|
<TextToolbarButton
|
|
|
|
onClick={ () =>
|
|
|
|
setAttributes( { mediaId: 0, mediaSrc: '' } )
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{ __( 'Reset', 'woo-gutenberg-products-block' ) }
|
|
|
|
</TextToolbarButton>
|
|
|
|
) : null }
|
|
|
|
</ToolbarGroup>
|
|
|
|
<ToolbarGroup
|
|
|
|
controls={ [
|
|
|
|
{
|
|
|
|
icon: 'edit',
|
|
|
|
title: editLabel,
|
|
|
|
onClick: () =>
|
|
|
|
setAttributes( { editMode: ! editMode } ),
|
|
|
|
isActive: editMode,
|
|
|
|
},
|
|
|
|
] }
|
|
|
|
/>
|
|
|
|
</BlockControlsWrapper>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-06-15 09:56:52 +00:00
|
|
|
export const withBlockControls =
|
|
|
|
( { cropLabel, editLabel }: BlockControlsConfiguration ) =>
|
|
|
|
< T extends EditorBlock< T > >( Component: ComponentType< T > ) =>
|
|
|
|
( props: WithBlockControlsProps< T > ) => {
|
|
|
|
const [ isEditingImage, setIsEditingImage ] = props.useEditingImage;
|
|
|
|
const { attributes, category, name, product, setAttributes } = props;
|
|
|
|
const { contentAlign, editMode, mediaId, mediaSrc } = attributes;
|
|
|
|
const item = category || product;
|
2022-05-19 16:16:46 +00:00
|
|
|
|
2022-06-15 09:56:52 +00:00
|
|
|
const { backgroundImageId, backgroundImageSrc } = useBackgroundImage( {
|
|
|
|
item,
|
|
|
|
mediaId,
|
|
|
|
mediaSrc,
|
|
|
|
blockName: name,
|
|
|
|
} );
|
2022-05-19 16:16:46 +00:00
|
|
|
|
2022-06-15 09:56:52 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<BlockControls
|
|
|
|
backgroundImageId={ backgroundImageId }
|
|
|
|
backgroundImageSrc={ backgroundImageSrc }
|
|
|
|
contentAlign={ contentAlign }
|
|
|
|
cropLabel={ cropLabel }
|
|
|
|
editLabel={ editLabel }
|
|
|
|
editMode={ editMode }
|
|
|
|
isEditingImage={ isEditingImage }
|
|
|
|
mediaSrc={ mediaSrc }
|
|
|
|
setAttributes={ setAttributes }
|
|
|
|
setIsEditingImage={ setIsEditingImage }
|
|
|
|
/>
|
|
|
|
<Component { ...props } />
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|