initial commit
This commit is contained in:
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import ErrorPlaceholder from '@woocommerce/editor-components/error-placeholder';
|
||||
|
||||
/**
|
||||
* Shown when there is an API error getting a product.
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {string} props.error
|
||||
* @param {boolean} props.isLoading
|
||||
* @param {function(any):any} props.getProduct
|
||||
*/
|
||||
const ApiError = ( { error, isLoading, getProduct } ) => (
|
||||
<ErrorPlaceholder
|
||||
className="wc-block-single-product-error"
|
||||
error={ error }
|
||||
isLoading={ isLoading }
|
||||
onRetry={ getProduct }
|
||||
/>
|
||||
);
|
||||
|
||||
export default ApiError;
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { BlockControls } from '@wordpress/block-editor';
|
||||
import { ToolbarGroup } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Adds controls to the editor toolbar.
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {boolean} props.isEditing
|
||||
* @param {function(boolean):any} props.setIsEditing
|
||||
*/
|
||||
const EditorBlockControls = ( { isEditing, setIsEditing } ) => {
|
||||
return (
|
||||
<BlockControls>
|
||||
<ToolbarGroup
|
||||
controls={ [
|
||||
{
|
||||
icon: 'edit',
|
||||
title: __( 'Edit', 'woocommerce' ),
|
||||
onClick: () => setIsEditing( ! isEditing ),
|
||||
isActive: isEditing,
|
||||
},
|
||||
] }
|
||||
/>
|
||||
</BlockControls>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditorBlockControls;
|
@ -0,0 +1,18 @@
|
||||
.wc-block-single-product__selection {
|
||||
width: 100%;
|
||||
}
|
||||
.wc-block-single-product__reset-layout {
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
.wc-block-single-product__edit-card {
|
||||
padding: 16px;
|
||||
border-top: 1px solid $gray-200;
|
||||
|
||||
.wc-block-single-product__edit-card-title {
|
||||
margin: 0 0 $gap;
|
||||
}
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState } from '@wordpress/element';
|
||||
import { Placeholder, Button, PanelBody } from '@wordpress/components';
|
||||
import { withProduct } from '@woocommerce/block-hocs';
|
||||
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
|
||||
import EditProductLink from '@woocommerce/editor-components/edit-product-link';
|
||||
import { singleProductBlockPreview } from '@woocommerce/resource-previews';
|
||||
import { InspectorControls } from '@wordpress/block-editor';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './editor.scss';
|
||||
import ApiError from './api-error';
|
||||
import SharedProductControl from './shared-product-control';
|
||||
import EditorBlockControls from './editor-block-controls';
|
||||
import LayoutEditor from './layout-editor';
|
||||
import { BLOCK_TITLE, BLOCK_ICON, BLOCK_DESCRIPTION } from '../constants';
|
||||
|
||||
/**
|
||||
* Component to handle edit mode of the "Single Product Block".
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {string} props.className
|
||||
* @param {Object} props.attributes Incoming block attributes.
|
||||
* @param {function(any):any} props.setAttributes Setter for block attributes.
|
||||
* @param {string} props.error
|
||||
* @param {function(any):any} props.getProduct
|
||||
* @param {Object} props.product
|
||||
* @param {boolean} props.isLoading
|
||||
* @param {string} props.clientId
|
||||
*/
|
||||
const Editor = ( {
|
||||
className,
|
||||
attributes,
|
||||
setAttributes,
|
||||
error,
|
||||
getProduct,
|
||||
product,
|
||||
isLoading,
|
||||
clientId,
|
||||
} ) => {
|
||||
const { productId, isPreview } = attributes;
|
||||
const [ isEditing, setIsEditing ] = useState( ! productId );
|
||||
|
||||
if ( isPreview ) {
|
||||
return singleProductBlockPreview;
|
||||
}
|
||||
|
||||
if ( error ) {
|
||||
return (
|
||||
<ApiError
|
||||
error={ error }
|
||||
isLoading={ isLoading }
|
||||
getProduct={ getProduct }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ className }>
|
||||
<BlockErrorBoundary
|
||||
header={ __(
|
||||
'Single Product Block Error',
|
||||
'woocommerce'
|
||||
) }
|
||||
>
|
||||
<EditorBlockControls
|
||||
setIsEditing={ setIsEditing }
|
||||
isEditing={ isEditing }
|
||||
/>
|
||||
{ isEditing ? (
|
||||
<Placeholder
|
||||
icon={ BLOCK_ICON }
|
||||
label={ BLOCK_TITLE }
|
||||
className="wc-block-single-product"
|
||||
>
|
||||
{ BLOCK_DESCRIPTION }
|
||||
<div className="wc-block-single-product__selection">
|
||||
<SharedProductControl
|
||||
attributes={ attributes }
|
||||
setAttributes={ setAttributes }
|
||||
/>
|
||||
<Button
|
||||
isSecondary
|
||||
onClick={ () => {
|
||||
setIsEditing( false );
|
||||
} }
|
||||
>
|
||||
{ __( 'Done', 'woocommerce' ) }
|
||||
</Button>
|
||||
</div>
|
||||
</Placeholder>
|
||||
) : (
|
||||
<>
|
||||
<InspectorControls>
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Product',
|
||||
'woocommerce'
|
||||
) }
|
||||
initialOpen={ false }
|
||||
>
|
||||
<SharedProductControl
|
||||
attributes={ attributes }
|
||||
setAttributes={ setAttributes }
|
||||
/>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
<EditProductLink productId={ productId } />
|
||||
<LayoutEditor
|
||||
clientId={ clientId }
|
||||
product={ product }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
</>
|
||||
) }
|
||||
</BlockErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withProduct( Editor );
|
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useCallback } from '@wordpress/element';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { InnerBlocks, InspectorControls } from '@wordpress/block-editor';
|
||||
import {
|
||||
InnerBlockLayoutContextProvider,
|
||||
ProductDataContextProvider,
|
||||
} from '@woocommerce/shared-context';
|
||||
import { createBlocksFromTemplate } from '@woocommerce/atomic-utils';
|
||||
import { PanelBody, Button } from '@wordpress/components';
|
||||
import { Icon, restore } from '@woocommerce/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
BLOCK_NAME,
|
||||
DEFAULT_INNER_BLOCKS,
|
||||
ALLOWED_INNER_BLOCKS,
|
||||
} from '../constants';
|
||||
|
||||
/**
|
||||
* Component to handle edit mode of the "Single Product Block".
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {boolean} props.isLoading
|
||||
* @param {Object} props.product
|
||||
* @param {string} props.clientId
|
||||
*/
|
||||
const LayoutEditor = ( { isLoading, product, clientId } ) => {
|
||||
const baseClassName = 'wc-block-single-product wc-block-layout';
|
||||
const { replaceInnerBlocks } = useDispatch( 'core/block-editor' );
|
||||
|
||||
const resetInnerBlocks = useCallback( () => {
|
||||
replaceInnerBlocks(
|
||||
clientId,
|
||||
createBlocksFromTemplate( DEFAULT_INNER_BLOCKS ),
|
||||
false
|
||||
);
|
||||
}, [ clientId, replaceInnerBlocks ] );
|
||||
|
||||
return (
|
||||
<InnerBlockLayoutContextProvider
|
||||
parentName={ BLOCK_NAME }
|
||||
parentClassName={ baseClassName }
|
||||
>
|
||||
<ProductDataContextProvider
|
||||
product={ product }
|
||||
isLoading={ isLoading }
|
||||
>
|
||||
<InspectorControls>
|
||||
<PanelBody
|
||||
title={ __( 'Layout', 'woocommerce' ) }
|
||||
initialOpen={ true }
|
||||
>
|
||||
<Button
|
||||
label={ __(
|
||||
'Reset layout to default',
|
||||
'woocommerce'
|
||||
) }
|
||||
onClick={ resetInnerBlocks }
|
||||
isTertiary
|
||||
className="wc-block-single-product__reset-layout"
|
||||
>
|
||||
<Icon srcElement={ restore } />{ ' ' }
|
||||
{ __(
|
||||
'Reset layout',
|
||||
'woocommerce'
|
||||
) }
|
||||
</Button>
|
||||
</PanelBody>
|
||||
</InspectorControls>
|
||||
<div className={ baseClassName }>
|
||||
<InnerBlocks
|
||||
template={ DEFAULT_INNER_BLOCKS }
|
||||
allowedBlocks={ ALLOWED_INNER_BLOCKS }
|
||||
templateLock={ false }
|
||||
renderAppender={ false }
|
||||
/>
|
||||
</div>
|
||||
</ProductDataContextProvider>
|
||||
</InnerBlockLayoutContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutEditor;
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import ProductControl from '@woocommerce/editor-components/product-control';
|
||||
|
||||
/**
|
||||
* Allows a product to be selected for display.
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {Object} props.attributes Incoming block attributes.
|
||||
* @param {function(any):any} props.setAttributes Setter for block attributes.
|
||||
*/
|
||||
const SharedProductControl = ( { attributes, setAttributes } ) => (
|
||||
<ProductControl
|
||||
selected={ attributes.productId || 0 }
|
||||
showVariations
|
||||
onChange={ ( value = [] ) => {
|
||||
const id = value[ 0 ] ? value[ 0 ].id : 0;
|
||||
setAttributes( {
|
||||
productId: id,
|
||||
} );
|
||||
} }
|
||||
/>
|
||||
);
|
||||
|
||||
export default SharedProductControl;
|
Reference in New Issue
Block a user