woocommerce/plugins/woocommerce-blocks/assets/js/blocks/featured-items/block-controls.tsx

175 lines
4.4 KiB
TypeScript
Raw Normal View History

/**
* External dependencies
*/
import { ComponentType, Dispatch, SetStateAction } from 'react';
import { __ } from '@wordpress/i18n';
import {
AlignmentToolbar,
BlockControls as BlockControlsWrapper,
MediaReplaceFlow,
} from '@wordpress/block-editor';
import type { BlockAlignment } from '@wordpress/blocks';
import { ToolbarButton, ToolbarGroup } from '@wordpress/components';
import { crop } from '@wordpress/icons';
import { WP_REST_API_Category } from 'wp-types';
import { ProductResponseItem } from '@woocommerce/types';
import TextToolbarButton from '@woocommerce/editor-components/text-toolbar-button';
/**
* Internal dependencies
*/
import { useBackgroundImage } from './use-background-image';
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;
}
export const BlockControls = ( {
backgroundImageId,
backgroundImageSrc,
contentAlign,
cropLabel,
editLabel,
editMode,
isEditingImage,
mediaSrc,
setAttributes,
setIsEditingImage,
}: BlockControlsProps ) => {
return (
<BlockControlsWrapper>
<AlignmentToolbar
value={ contentAlign }
onChange={ ( nextAlign: BlockAlignment ) => {
setAttributes( { contentAlign: nextAlign } );
} }
/>
<ToolbarGroup>
{ backgroundImageSrc && ! isEditingImage && (
<ToolbarButton
onClick={ () => setIsEditingImage( true ) }
icon={ crop }
label={ cropLabel }
/>
) }
<MediaReplaceFlow
mediaId={ backgroundImageId }
mediaURL={ mediaSrc }
accept="image/*"
onSelect={ ( media: Media ) => {
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>
);
};
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;
const { backgroundImageId, backgroundImageSrc } = useBackgroundImage( {
item,
mediaId,
mediaSrc,
blockName: name,
} );
return (
<>
<BlockControls
backgroundImageId={ backgroundImageId }
backgroundImageSrc={ backgroundImageSrc }
contentAlign={ contentAlign }
cropLabel={ cropLabel }
editLabel={ editLabel }
editMode={ editMode }
isEditingImage={ isEditingImage }
mediaSrc={ mediaSrc }
setAttributes={ setAttributes }
setIsEditingImage={ setIsEditingImage }
/>
<Component { ...props } />
</>
);
};