⚛️ Introduce Product Element Blocks (https://github.com/woocommerce/woocommerce-blocks/pull/2871)
* Product selection when out of context for price and title blocks * Move product element name/description/icons to constant files * Add attributes and hocs to all elements * Standalone block rendering * Add a placeholder if title has no content * Revert "Add a placeholder if title has no content" This reverts commit 29115154b33eedc661ccd3cc758acdbc5041ffbc. * parentClassName is not always present * Loading state * Wrap description in P * Fixed loading styles when nested * Maintain product shape in useProductData * feature gate elements from showing in inserter * fix feature flag * include price PR * edit withProductSelector to be a hoc * fix lint issue Co-authored-by: Seghir Nadir <nadir.seghir@gmail.com>
This commit is contained in:
parent
ff2f135e7e
commit
cd9f7e0ccb
|
@ -236,7 +236,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wc-block-grid__products .wc-block-grid__product-onsale,
|
.wc-block-grid__products .wc-block-grid__product-onsale,
|
||||||
.wc-block-layout .wc-block-components-product-sale-badge {
|
.wc-block-components-product-sale-badge {
|
||||||
background: $twentytwenty-highlights-color;
|
background: $twentytwenty-highlights-color;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: $twentytwenty-headings;
|
font-family: $twentytwenty-headings;
|
||||||
|
|
|
@ -13,7 +13,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-price',
|
blockName: 'woocommerce/product-price',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/price" */ './product/price/block'
|
/* webpackChunkName: "atomic-block-components/price" */ './product-elements/price/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -22,7 +22,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-image',
|
blockName: 'woocommerce/product-image',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/image" */ './product/image/frontend'
|
/* webpackChunkName: "atomic-block-components/image" */ './product-elements/image/frontend'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -31,7 +31,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-title',
|
blockName: 'woocommerce/product-title',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/title" */ './product/title/frontend'
|
/* webpackChunkName: "atomic-block-components/title" */ './product-elements/title/frontend'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -40,7 +40,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-rating',
|
blockName: 'woocommerce/product-rating',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/rating" */ './product/rating/block'
|
/* webpackChunkName: "atomic-block-components/rating" */ './product-elements/rating/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -49,7 +49,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-button',
|
blockName: 'woocommerce/product-button',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/button" */ './product/button/block'
|
/* webpackChunkName: "atomic-block-components/button" */ './product-elements/button/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -58,7 +58,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-summary',
|
blockName: 'woocommerce/product-summary',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/summary" */ './product/summary/block'
|
/* webpackChunkName: "atomic-block-components/summary" */ './product-elements/summary/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -67,7 +67,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-sale-badge',
|
blockName: 'woocommerce/product-sale-badge',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/sale-badge" */ './product/sale-badge/block'
|
/* webpackChunkName: "atomic-block-components/sale-badge" */ './product-elements/sale-badge/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -76,7 +76,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-sku',
|
blockName: 'woocommerce/product-sku',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/sku" */ './product/sku/block'
|
/* webpackChunkName: "atomic-block-components/sku" */ './product-elements/sku/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -85,7 +85,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-category-list',
|
blockName: 'woocommerce/product-category-list',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/category-list" */ './product/category-list/block'
|
/* webpackChunkName: "atomic-block-components/category-list" */ './product-elements/category-list/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -94,7 +94,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-tag-list',
|
blockName: 'woocommerce/product-tag-list',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/tag-list" */ './product/tag-list/block'
|
/* webpackChunkName: "atomic-block-components/tag-list" */ './product-elements/tag-list/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -103,7 +103,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-stock-indicator',
|
blockName: 'woocommerce/product-stock-indicator',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/stock-indicator" */ './product/stock-indicator/block'
|
/* webpackChunkName: "atomic-block-components/stock-indicator" */ './product-elements/stock-indicator/block'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
@ -112,7 +112,7 @@ registerBlockComponent( {
|
||||||
blockName: 'woocommerce/product-add-to-cart',
|
blockName: 'woocommerce/product-add-to-cart',
|
||||||
component: lazy( () =>
|
component: lazy( () =>
|
||||||
import(
|
import(
|
||||||
/* webpackChunkName: "atomic-block-components/add-to-cart" */ './product/add-to-cart/frontend'
|
/* webpackChunkName: "atomic-block-components/add-to-cart" */ './product-elements/add-to-cart/frontend'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import './product/title';
|
import './product-elements/title';
|
||||||
import './product/price';
|
import './product-elements/price';
|
||||||
import './product/image';
|
import './product-elements/image';
|
||||||
import './product/rating';
|
import './product-elements/rating';
|
||||||
import './product/button';
|
import './product-elements/button';
|
||||||
import './product/summary';
|
import './product-elements/summary';
|
||||||
import './product/sale-badge';
|
import './product-elements/sale-badge';
|
||||||
import './product/sku';
|
import './product-elements/sku';
|
||||||
import './product/category-list';
|
import './product-elements/category-list';
|
||||||
import './product/tag-list';
|
import './product-elements/tag-list';
|
||||||
import './product/stock-indicator';
|
import './product-elements/stock-indicator';
|
||||||
import './product/add-to-cart';
|
import './product-elements/add-to-cart';
|
||||||
|
|
|
@ -3,6 +3,10 @@ export const blockAttributes = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default blockAttributes;
|
export default blockAttributes;
|
|
@ -6,6 +6,7 @@ import classnames from 'classnames';
|
||||||
import { AddToCartFormContextProvider } from '@woocommerce/base-context';
|
import { AddToCartFormContextProvider } from '@woocommerce/base-context';
|
||||||
import { useProductDataContext } from '@woocommerce/shared-context';
|
import { useProductDataContext } from '@woocommerce/shared-context';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -25,13 +26,10 @@ import {
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {boolean} [props.showFormElements] Should form elements be shown?
|
* @param {boolean} [props.showFormElements] Should form elements be shown?
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be
|
|
||||||
* used if this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, showFormElements, ...props } ) => {
|
const Block = ( { className, showFormElements } ) => {
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product || {};
|
|
||||||
const componentClass = classnames(
|
const componentClass = classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-add-to-cart',
|
'wc-block-components-product-add-to-cart',
|
||||||
|
@ -50,9 +48,7 @@ const Block = ( { className, showFormElements, ...props } ) => {
|
||||||
<div className={ componentClass }>
|
<div className={ componentClass }>
|
||||||
<>
|
<>
|
||||||
{ showFormElements ? (
|
{ showFormElements ? (
|
||||||
<AddToCartForm
|
<AddToCartForm productType={ product.type } />
|
||||||
productType={ product.type || 'simple' }
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<AddToCartButton />
|
<AddToCartButton />
|
||||||
) }
|
) }
|
||||||
|
@ -80,7 +76,6 @@ const AddToCartForm = ( { productType } ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { cart, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __( 'Add to Cart', 'woo-gutenberg-products-block' );
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ cart } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Displays an add to cart button. Optionally displays other add to cart form elements.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -13,10 +13,11 @@ import { InspectorControls } from '@wordpress/block-editor';
|
||||||
*/
|
*/
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
import Block from './block';
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
export default ( { attributes, setAttributes } ) => {
|
const Edit = ( { attributes, setAttributes } ) => {
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = productDataContext.product || {};
|
|
||||||
const { className, showFormElements } = attributes;
|
const { className, showFormElements } = attributes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -26,7 +27,7 @@ export default ( { attributes, setAttributes } ) => {
|
||||||
'wc-block-components-product-add-to-cart'
|
'wc-block-components-product-add-to-cart'
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
<EditProductLink productId={ product.id || 0 } />
|
<EditProductLink productId={ product.id } />
|
||||||
{ product.type !== 'external' && (
|
{ product.type !== 'external' && (
|
||||||
<InspectorControls>
|
<InspectorControls>
|
||||||
<PanelBody
|
<PanelBody
|
||||||
|
@ -57,3 +58,12 @@ export default ( { attributes, setAttributes } ) => {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's add to cart form.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import edit from './edit';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
edit,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerExperimentalBlockType( 'woocommerce/product-add-to-cart', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -0,0 +1,43 @@
|
||||||
|
.wc-block-components-product-add-to-cart {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.wc-block-components-product-add-to-cart-button {
|
||||||
|
margin: 0 0 em($gap-small) 0;
|
||||||
|
|
||||||
|
.wc-block-components-button__text {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
> svg {
|
||||||
|
fill: currentColor;
|
||||||
|
vertical-align: top;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
margin: -0.25em 0 -0.25em 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wc-block-components-product-add-to-cart-quantity {
|
||||||
|
margin: 0 1em em($gap-small) 0;
|
||||||
|
width: 5em;
|
||||||
|
padding: 0.618em;
|
||||||
|
background: $white;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #43454b;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.125);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wc-block-components-product-add-to-cart--placeholder {
|
||||||
|
.wc-block-components-product-add-to-cart-quantity,
|
||||||
|
.wc-block-components-product-add-to-cart-button {
|
||||||
|
@include placeholder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wc-block-grid .wc-block-components-product-add-to-cart {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -12,6 +12,7 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -23,14 +24,11 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -38,10 +36,12 @@ const Block = ( { className, ...props } ) => {
|
||||||
className,
|
className,
|
||||||
'wp-block-button',
|
'wp-block-button',
|
||||||
'wc-block-components-product-button',
|
'wc-block-components-product-button',
|
||||||
`${ parentClassName }__product-add-to-cart`
|
{
|
||||||
|
[ `${ parentClassName }__product-add-to-cart` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
{ product ? (
|
{ product.id ? (
|
||||||
<AddToCartButton product={ product } />
|
<AddToCartButton product={ product } />
|
||||||
) : (
|
) : (
|
||||||
<AddToCartButtonPlaceholder />
|
<AddToCartButtonPlaceholder />
|
||||||
|
@ -142,7 +142,6 @@ const AddToCartButtonPlaceholder = () => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { cart, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Add to Cart Button',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ cart } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display a call to action button which either adds the product to the cart, or links to the product page.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Disabled } from '@wordpress/components';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return (
|
||||||
|
<Disabled>
|
||||||
|
<Block { ...attributes } />
|
||||||
|
</Disabled>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's add to cart button.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-button', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -1,4 +1,4 @@
|
||||||
.wc-block-layout .wc-block-components-product-button {
|
.wp-block-button.wc-block-components-product-button {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wc-block-layout--is-loading .wc-block-components-product-button > .wc-block-components-product-button__button {
|
.is-loading .wc-block-components-product-button > .wc-block-components-product-button__button {
|
||||||
@include placeholder();
|
@include placeholder();
|
||||||
min-width: 8em;
|
min-width: 8em;
|
||||||
min-height: 3em;
|
min-height: 3em;
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -9,6 +9,7 @@ import {
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -20,16 +21,13 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const { product } = productDataContext || props || {};
|
|
||||||
|
|
||||||
if ( isEmpty( product ) || isEmpty( product.categories ) ) {
|
if ( isEmpty( product.categories ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +36,9 @@ const Block = ( { className, ...props } ) => {
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-category-list',
|
'wc-block-components-product-category-list',
|
||||||
`${ parentClassName }__product-category-list`
|
{
|
||||||
|
[ `${ parentClassName }__product-category-list` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
{ __( 'Categories:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
{ __( 'Categories:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
||||||
|
@ -59,7 +59,6 @@ const Block = ( { className, ...props } ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { folder, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Category List',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ folder } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display a list of categories belonging to a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Disabled } from '@wordpress/components';
|
||||||
|
import EditProductLink from '@woocommerce/block-components/edit-product-link';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EditProductLink />
|
||||||
|
<Disabled>
|
||||||
|
<Block { ...attributes } />
|
||||||
|
</Disabled>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's categories.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerExperimentalBlockType( 'woocommerce/product-category-list', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -1,4 +1,4 @@
|
||||||
.wc-block-layout .wc-block-components-product-tag-list {
|
.wc-block-components-product-category-list {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: em($gap-small);
|
margin-bottom: em($gap-small);
|
||||||
|
|
|
@ -15,6 +15,10 @@ export const blockAttributes = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: 'full-size',
|
default: 'full-size',
|
||||||
},
|
},
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default blockAttributes;
|
export default blockAttributes;
|
|
@ -9,6 +9,8 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -21,11 +23,10 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
|
* @param {string} [props.imageSizing] Size of image to use.
|
||||||
* @param {boolean} [props.productLink] Whether or not to display a link to the product page.
|
* @param {boolean} [props.productLink] Whether or not to display a link to the product page.
|
||||||
* @param {boolean} [props.showSaleBadge] Whether or not to display the on sale badge.
|
* @param {boolean} [props.showSaleBadge] Whether or not to display the on sale badge.
|
||||||
* @param {string} [props.saleBadgeAlign] How should the sale badge be aligned if displayed.
|
* @param {string} [props.saleBadgeAlign] How should the sale badge be aligned if displayed.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( {
|
const Block = ( {
|
||||||
|
@ -34,21 +35,21 @@ const Block = ( {
|
||||||
productLink = true,
|
productLink = true,
|
||||||
showSaleBadge,
|
showSaleBadge,
|
||||||
saleBadgeAlign = 'right',
|
saleBadgeAlign = 'right',
|
||||||
...props
|
|
||||||
} ) => {
|
} ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product;
|
|
||||||
const [ imageLoaded, setImageLoaded ] = useState( false );
|
const [ imageLoaded, setImageLoaded ] = useState( false );
|
||||||
|
|
||||||
if ( ! product ) {
|
if ( ! product.id ) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-image',
|
'wc-block-components-product-image',
|
||||||
'wc-block-components-product-image--placeholder',
|
'wc-block-components-product-image--placeholder',
|
||||||
`${ parentClassName }__product-image`
|
{
|
||||||
|
[ `${ parentClassName }__product-image` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
<ImagePlaceholder />
|
<ImagePlaceholder />
|
||||||
|
@ -56,15 +57,16 @@ const Block = ( {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const image =
|
const image = ! isEmpty( product.images ) ? product.images[ 0 ] : null;
|
||||||
product?.images && product.images.length ? product.images[ 0 ] : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-image',
|
'wc-block-components-product-image',
|
||||||
`${ parentClassName }__product-image`
|
{
|
||||||
|
[ `${ parentClassName }__product-image` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
{ productLink ? (
|
{ productLink ? (
|
||||||
|
@ -103,7 +105,9 @@ const Block = ( {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ImagePlaceholder = () => {
|
const ImagePlaceholder = () => {
|
||||||
return <img src={ PLACEHOLDER_IMG_SRC } alt="" />;
|
return (
|
||||||
|
<img src={ PLACEHOLDER_IMG_SRC } alt="" width={ 500 } height={ 500 } />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Image = ( { image, onLoad, loaded, showFullSize } ) => {
|
const Image = ( { image, onLoad, loaded, showFullSize } ) => {
|
||||||
|
@ -135,10 +139,9 @@ const Image = ( { image, onLoad, loaded, showFullSize } ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
productLink: PropTypes.bool,
|
productLink: PropTypes.bool,
|
||||||
showSaleBadge: PropTypes.bool,
|
showSaleBadge: PropTypes.bool,
|
||||||
saleBadgeAlign: PropTypes.string,
|
saleBadgeAlign: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { image, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Image',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ image } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display the main product image',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -12,8 +12,10 @@ import { getAdminLink } from '@woocommerce/settings';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import Block from './block';
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
export default ( { attributes, setAttributes } ) => {
|
const Edit = ( { attributes, setAttributes } ) => {
|
||||||
const {
|
const {
|
||||||
productLink,
|
productLink,
|
||||||
imageSizing,
|
imageSizing,
|
||||||
|
@ -146,3 +148,12 @@ export default ( { attributes, setAttributes } ) => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's image.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-image', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -1,5 +1,5 @@
|
||||||
.editor-styles-wrapper .wc-block-layout .wc-block-grid__products .wc-block-grid__product .wc-block-components-product-image,
|
.editor-styles-wrapper .wc-block-grid__products .wc-block-grid__product .wc-block-components-product-image,
|
||||||
.wc-block-layout .wc-block-components-product-image {
|
.wc-block-components-product-image {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: $gap-small;
|
margin-bottom: $gap-small;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -48,6 +48,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wc-block-layout--is-loading .wc-block-components-product-image {
|
.is-loading .wc-block-components-product-image {
|
||||||
@include placeholder();
|
@include placeholder();
|
||||||
}
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
let blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( isFeaturePluginBuild() ) {
|
||||||
|
blockAttributes = {
|
||||||
|
...blockAttributes,
|
||||||
|
align: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
customFontSize: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
saleFontSize: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
customSaleFontSize: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
saleColor: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
customColor: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
customSaleColor: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default blockAttributes;
|
|
@ -11,6 +11,8 @@ import {
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
|
import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
|
||||||
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
|
@ -45,11 +47,9 @@ const Block = ( {
|
||||||
customColor,
|
customColor,
|
||||||
saleColor,
|
saleColor,
|
||||||
customSaleColor,
|
customSaleColor,
|
||||||
...props
|
|
||||||
} ) => {
|
} ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product;
|
|
||||||
|
|
||||||
const colorClass = getColorClassName( 'color', color );
|
const colorClass = getColorClassName( 'color', color );
|
||||||
const fontSizeClass = getFontSizeClass( fontSize );
|
const fontSizeClass = getFontSizeClass( fontSize );
|
||||||
|
@ -80,20 +80,22 @@ const Block = ( {
|
||||||
fontSize: customSaleFontSize,
|
fontSize: customSaleFontSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( ! product ) {
|
if ( ! product.id ) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'price',
|
'price',
|
||||||
'wc-block-components-product-price',
|
'wc-block-components-product-price',
|
||||||
`${ parentClassName }__product-price`
|
{
|
||||||
|
[ `${ parentClassName }__product-price` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const prices = product.prices || {};
|
const prices = product.prices;
|
||||||
const currency = getCurrencyFromPriceResponse( prices );
|
const currency = getCurrencyFromPriceResponse( prices );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -102,8 +104,8 @@ const Block = ( {
|
||||||
className,
|
className,
|
||||||
'price',
|
'price',
|
||||||
'wc-block-components-product-price',
|
'wc-block-components-product-price',
|
||||||
`${ parentClassName }__product-price`,
|
|
||||||
{
|
{
|
||||||
|
[ `${ parentClassName }__product-price` ]: parentClassName,
|
||||||
[ `wc-block-components-product-price__align-${ align }` ]:
|
[ `wc-block-components-product-price__align-${ align }` ]:
|
||||||
align && isFeaturePluginBuild(),
|
align && isFeaturePluginBuild(),
|
||||||
}
|
}
|
||||||
|
@ -155,8 +157,10 @@ const PriceRange = ( { currency, minAmount, maxAmount, classes, style } ) => {
|
||||||
<span
|
<span
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
'wc-block-components-product-price__value',
|
'wc-block-components-product-price__value',
|
||||||
`${ parentClassName }__product-price__value`,
|
{
|
||||||
{ [ classes ]: isFeaturePluginBuild() }
|
[ `${ parentClassName }__product-price__value` ]: parentClassName,
|
||||||
|
[ classes ]: isFeaturePluginBuild()
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
style={ isFeaturePluginBuild() ? style : {} }
|
style={ isFeaturePluginBuild() ? style : {} }
|
||||||
>
|
>
|
||||||
|
@ -188,8 +192,9 @@ const SalePrice = ( {
|
||||||
<del
|
<del
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
'wc-block-components-product-price__regular',
|
'wc-block-components-product-price__regular',
|
||||||
`${ parentClassName }__product-price__regular`,
|
{
|
||||||
{ [ classes ]: isFeaturePluginBuild() }
|
[ `${ parentClassName }__product-price__regular` ]: parentClassName,
|
||||||
|
[ classes ]: isFeaturePluginBuild() }
|
||||||
) }
|
) }
|
||||||
style={ isFeaturePluginBuild() ? style : {} }
|
style={ isFeaturePluginBuild() ? style : {} }
|
||||||
>
|
>
|
||||||
|
@ -201,8 +206,9 @@ const SalePrice = ( {
|
||||||
<span
|
<span
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
'wc-block-components-product-price__value',
|
'wc-block-components-product-price__value',
|
||||||
`${ parentClassName }__product-price__value`,
|
{
|
||||||
{ [ saleClasses ]: isFeaturePluginBuild() }
|
[ `${ parentClassName }__product-price__value` ]: parentClassName,
|
||||||
|
[ saleClasses ]: isFeaturePluginBuild() }
|
||||||
) }
|
) }
|
||||||
style={ isFeaturePluginBuild() ? saleStyle : {} }
|
style={ isFeaturePluginBuild() ? saleStyle : {} }
|
||||||
>
|
>
|
||||||
|
@ -245,4 +251,4 @@ Block.propTypes = {
|
||||||
customSaleColor: PropTypes.string,
|
customSaleColor: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { bill, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Price',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ bill } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display the price of a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -18,6 +18,8 @@ import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import Block from './block';
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
const TextControl = ( {
|
const TextControl = ( {
|
||||||
fontSize,
|
fontSize,
|
||||||
|
@ -117,6 +119,14 @@ const Price = isFeaturePluginBuild()
|
||||||
withColors( 'color', { textColor: 'color' } ),
|
withColors( 'color', { textColor: 'color' } ),
|
||||||
withColors( 'saleColor', { textColor: 'saleColor' } ),
|
withColors( 'saleColor', { textColor: 'saleColor' } ),
|
||||||
withColors( 'originalColor', { textColor: 'originalColor' } ),
|
withColors( 'originalColor', { textColor: 'originalColor' } ),
|
||||||
|
withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's price.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} ),
|
||||||
] )( PriceEdit )
|
] )( PriceEdit )
|
||||||
: PriceEdit;
|
: PriceEdit;
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import edit from './edit';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-price', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -0,0 +1,28 @@
|
||||||
|
.wc-block-components-product-price {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: $gap-small;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&__regular {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.is-loading {
|
||||||
|
.wc-block-components-product-price::before {
|
||||||
|
@include placeholder();
|
||||||
|
content: ".";
|
||||||
|
display: inline-block;
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
/*rtl:begin:ignore*/
|
||||||
|
.wc-block-components-product-price__align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.wc-block-components-product-price__align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wc-block-components-product-price__align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
/*rtl:end:ignore*/
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -8,6 +8,7 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -19,14 +20,11 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product;
|
|
||||||
const rating = getAverageRating( product );
|
const rating = getAverageRating( product );
|
||||||
|
|
||||||
if ( ! rating ) {
|
if ( ! rating ) {
|
||||||
|
@ -48,7 +46,9 @@ const Block = ( { className, ...props } ) => {
|
||||||
className,
|
className,
|
||||||
'star-rating',
|
'star-rating',
|
||||||
'wc-block-components-product-rating',
|
'wc-block-components-product-rating',
|
||||||
`${ parentClassName }__product-rating`
|
{
|
||||||
|
[ `${ parentClassName }__product-rating` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -67,14 +67,13 @@ const Block = ( { className, ...props } ) => {
|
||||||
|
|
||||||
const getAverageRating = ( product ) => {
|
const getAverageRating = ( product ) => {
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
const rating = parseFloat( product?.average_rating || 0 );
|
const rating = parseFloat( product.average_rating );
|
||||||
|
|
||||||
return Number.isFinite( rating ) && rating > 0 ? rating : 0;
|
return Number.isFinite( rating ) && rating > 0 ? rating : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { star, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Rating',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ star } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display the average rating of a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return <Block { ...attributes } />;
|
||||||
|
};
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's rating.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-rating', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -0,0 +1,52 @@
|
||||||
|
.wc-block-components-product-rating {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: $gap-small;
|
||||||
|
|
||||||
|
&__stars {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
width: 5.3em;
|
||||||
|
height: 1.618em;
|
||||||
|
line-height: 1.618;
|
||||||
|
font-size: 1em;
|
||||||
|
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
|
||||||
|
font-family: star;
|
||||||
|
font-weight: 400;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "\53\53\53\53\53";
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0.5;
|
||||||
|
color: #aaa;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
overflow: hidden;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
position: absolute;
|
||||||
|
padding-top: 1.5em;
|
||||||
|
}
|
||||||
|
span::before {
|
||||||
|
content: "\53\53\53\53\53";
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
position: absolute;
|
||||||
|
color: #000;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wc-block-single-product {
|
||||||
|
.wc-block-components-product-rating__stars {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -9,6 +9,7 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -21,16 +22,13 @@ import './style.scss';
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {string} [props.align] Alignment of the badge.
|
* @param {string} [props.align] Alignment of the badge.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, align, ...props } ) => {
|
const Block = ( { className, align } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product;
|
|
||||||
|
|
||||||
if ( ! product || ! product.on_sale ) {
|
if ( ! product.id || ! product.on_sale ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +43,9 @@ const Block = ( { className, align, ...props } ) => {
|
||||||
'wc-block-components-product-sale-badge',
|
'wc-block-components-product-sale-badge',
|
||||||
className,
|
className,
|
||||||
alignClass,
|
alignClass,
|
||||||
`${ parentClassName }__product-onsale`
|
{
|
||||||
|
[ `${ parentClassName }__product-onsale` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
<Label
|
<Label
|
||||||
|
@ -62,7 +62,6 @@ const Block = ( { className, align, ...props } ) => {
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
align: PropTypes.string,
|
align: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { tag, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'On-Sale Badge',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ tag } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Displays an on-sale badge if the product is on-sale.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return <Block { ...attributes } />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's sale-badge.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
supports: {
|
||||||
|
html: false,
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-sale-badge', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -3,17 +3,17 @@
|
||||||
*/
|
*/
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon, grid } from '@woocommerce/icons';
|
import { Icon, grid } from '@woocommerce/icons';
|
||||||
|
import { isExperimentalBuild } from '@woocommerce/block-settings';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import save from './save';
|
import save from '../save';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds default config for this collection of blocks.
|
* Holds default config for this collection of blocks.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
category: 'woocommerce',
|
category: 'woocommerce-product-elements',
|
||||||
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
|
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
|
||||||
icon: {
|
icon: {
|
||||||
src: <Icon srcElement={ grid } />,
|
src: <Icon srcElement={ grid } />,
|
||||||
|
@ -22,7 +22,9 @@ export default {
|
||||||
supports: {
|
supports: {
|
||||||
html: false,
|
html: false,
|
||||||
},
|
},
|
||||||
parent: [ 'woocommerce/all-products', 'woocommerce/single-product' ],
|
parent: isExperimentalBuild()
|
||||||
|
? null
|
||||||
|
: [ '@woocommerce/all-products', '@woocommerce/single-product' ],
|
||||||
save,
|
save,
|
||||||
deprecated: [
|
deprecated: [
|
||||||
{
|
{
|
|
@ -0,0 +1,11 @@
|
||||||
|
.wc-atomic-blocks-product__selection {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.wc-atomic-blocks-product__edit-card {
|
||||||
|
padding: 16px;
|
||||||
|
border-top: 1px solid #e2e4e7;
|
||||||
|
|
||||||
|
.wc-atomic-blocks-product__edit-card-title {
|
||||||
|
margin: 0 0 $gap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { useState } from '@wordpress/element';
|
||||||
|
import ProductControl from '@woocommerce/block-components/product-control';
|
||||||
|
import { Placeholder, Button, Toolbar } from '@wordpress/components';
|
||||||
|
import { BlockControls } from '@wordpress/block-editor';
|
||||||
|
import TextToolbarButton from '@woocommerce/block-components/text-toolbar-button';
|
||||||
|
import { useProductDataContext } from '@woocommerce/shared-context';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import './editor.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This HOC shows a product selection interface if context is not present in the editor.
|
||||||
|
*
|
||||||
|
* @param {Object} selectorArgs Options for the selector.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const withProductSelector = ( selectorArgs ) => ( OriginalComponent ) => {
|
||||||
|
return ( props ) => {
|
||||||
|
const productDataContext = useProductDataContext();
|
||||||
|
const { attributes, setAttributes } = props;
|
||||||
|
const { productId } = attributes;
|
||||||
|
const [ isEditing, setIsEditing ] = useState( ! productId );
|
||||||
|
|
||||||
|
if ( productDataContext.hasContext ) {
|
||||||
|
return <OriginalComponent { ...props } />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ isEditing ? (
|
||||||
|
<Placeholder
|
||||||
|
icon={ selectorArgs.icon || '' }
|
||||||
|
label={ selectorArgs.label || '' }
|
||||||
|
className="wc-atomic-blocks-product"
|
||||||
|
>
|
||||||
|
{ !! selectorArgs.description && (
|
||||||
|
<div>{ selectorArgs.description }</div>
|
||||||
|
) }
|
||||||
|
<div className="wc-atomic-blocks-product__selection">
|
||||||
|
<ProductControl
|
||||||
|
selected={ productId || 0 }
|
||||||
|
showVariations
|
||||||
|
onChange={ ( value = [] ) => {
|
||||||
|
setAttributes( {
|
||||||
|
productId: value[ 0 ]
|
||||||
|
? value[ 0 ].id
|
||||||
|
: 0,
|
||||||
|
} );
|
||||||
|
} }
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
isDefault
|
||||||
|
disabled={ ! productId }
|
||||||
|
onClick={ () => {
|
||||||
|
setIsEditing( false );
|
||||||
|
} }
|
||||||
|
>
|
||||||
|
{ __( 'Done', 'woo-gutenberg-products-block' ) }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Placeholder>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<BlockControls>
|
||||||
|
<Toolbar>
|
||||||
|
<TextToolbarButton
|
||||||
|
onClick={ () => setIsEditing( true ) }
|
||||||
|
>
|
||||||
|
{ __(
|
||||||
|
'Switch product…',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
) }
|
||||||
|
</TextToolbarButton>
|
||||||
|
</Toolbar>
|
||||||
|
</BlockControls>
|
||||||
|
<OriginalComponent { ...props } />
|
||||||
|
</>
|
||||||
|
) }
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector;
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -8,6 +8,7 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -19,15 +20,12 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product || {};
|
const sku = product.sku;
|
||||||
const sku = product.sku || '';
|
|
||||||
|
|
||||||
if ( ! sku ) {
|
if ( ! sku ) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -38,7 +36,9 @@ const Block = ( { className, ...props } ) => {
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-sku',
|
'wc-block-components-product-sku',
|
||||||
`${ parentClassName }__product-sku`
|
{
|
||||||
|
[ `${ parentClassName }__product-sku` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
{ __( 'SKU:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
{ __( 'SKU:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
||||||
|
@ -49,7 +49,6 @@ const Block = ( { className, ...props } ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { barcode, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __( 'Product SKU', 'woo-gutenberg-products-block' );
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ barcode } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display the SKU of a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import EditProductLink from '@woocommerce/block-components/edit-product-link';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EditProductLink />
|
||||||
|
<Block { ...attributes } />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's SKU.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerExperimentalBlockType( 'woocommerce/product-sku', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -0,0 +1,7 @@
|
||||||
|
.wc-block-components-product-sku {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: $gap-small;
|
||||||
|
display: block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include font-size(small);
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -8,7 +8,7 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
import { isEmpty } from 'lodash';
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -20,16 +20,13 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product || {};
|
|
||||||
|
|
||||||
if ( isEmpty( product ) || ! product.is_purchasable ) {
|
if ( ! product.id || ! product.is_purchasable ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,8 +39,8 @@ const Block = ( { className, ...props } ) => {
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-stock-indicator',
|
'wc-block-components-product-stock-indicator',
|
||||||
`${ parentClassName }__stock-indicator`,
|
|
||||||
{
|
{
|
||||||
|
[ `${ parentClassName }__stock-indicator` ]: parentClassName,
|
||||||
'wc-block-components-product-stock-indicator--in-stock': inStock,
|
'wc-block-components-product-stock-indicator--in-stock': inStock,
|
||||||
'wc-block-components-product-stock-indicator--out-of-stock': ! inStock,
|
'wc-block-components-product-stock-indicator--out-of-stock': ! inStock,
|
||||||
'wc-block-components-product-stock-indicator--low-stock': !! lowStock,
|
'wc-block-components-product-stock-indicator--low-stock': !! lowStock,
|
||||||
|
@ -78,7 +75,6 @@ const stockText = ( inStock, isBackordered ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { box, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Stock Indicator',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ box } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display product stock status.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import EditProductLink from '@woocommerce/block-components/edit-product-link';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EditProductLink />
|
||||||
|
<Block { ...attributes } />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's stock.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerExperimentalBlockType( 'woocommerce/product-stock-indicator', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -0,0 +1,17 @@
|
||||||
|
.wc-block-components-product-stock-indicator {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: em($gap-small);
|
||||||
|
display: block;
|
||||||
|
@include font-size(small);
|
||||||
|
|
||||||
|
&--in-stock {
|
||||||
|
color: $valid-green;
|
||||||
|
}
|
||||||
|
&--out-of-stock {
|
||||||
|
color: $error-red;
|
||||||
|
}
|
||||||
|
&--low-stock,
|
||||||
|
&--available-on-backorder {
|
||||||
|
color: $notice-yellow;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -9,6 +9,7 @@ import {
|
||||||
useInnerBlockLayoutContext,
|
useInnerBlockLayoutContext,
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -20,21 +21,21 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const { product } = productDataContext || props;
|
|
||||||
|
|
||||||
if ( ! product ) {
|
if ( ! product ) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
`wc-block-components-product-summary`
|
`wc-block-components-product-summary`,
|
||||||
|
{
|
||||||
|
[ `${ parentClassName }__product-summary` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -55,7 +56,9 @@ const Block = ( { className, ...props } ) => {
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
`wc-block-components-product-summary`,
|
`wc-block-components-product-summary`,
|
||||||
`${ parentClassName }__product-summary`
|
{
|
||||||
|
[ `${ parentClassName }__product-summary` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
source={ source }
|
source={ source }
|
||||||
maxLength={ 150 }
|
maxLength={ 150 }
|
||||||
|
@ -66,7 +69,6 @@ const Block = ( { className, ...props } ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { notes, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Summary',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ notes } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display a short description about a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return <Block { ...attributes } />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's short description.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-summary', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -1,9 +1,8 @@
|
||||||
.wc-block-layout .wc-block-components-product-summary {
|
.wc-block-components-product-summary {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: $gap-small;
|
margin-bottom: $gap-small;
|
||||||
}
|
}
|
||||||
|
.is-loading .wc-block-components-product-summary::before {
|
||||||
.wc-block-layout--is-loading .wc-block-components-product-summary::before {
|
|
||||||
@include placeholder();
|
@include placeholder();
|
||||||
content: ".";
|
content: ".";
|
||||||
display: block;
|
display: block;
|
|
@ -0,0 +1,8 @@
|
||||||
|
export const blockAttributes = {
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default blockAttributes;
|
|
@ -9,6 +9,7 @@ import {
|
||||||
useProductDataContext,
|
useProductDataContext,
|
||||||
} from '@woocommerce/shared-context';
|
} from '@woocommerce/shared-context';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -20,16 +21,13 @@ import './style.scss';
|
||||||
*
|
*
|
||||||
* @param {Object} props Incoming props.
|
* @param {Object} props Incoming props.
|
||||||
* @param {string} [props.className] CSS Class name for the component.
|
* @param {string} [props.className] CSS Class name for the component.
|
||||||
* @param {Object} [props.product] Optional product object. Product from context will be used if
|
|
||||||
* this is not provided.
|
|
||||||
* @return {*} The component.
|
* @return {*} The component.
|
||||||
*/
|
*/
|
||||||
const Block = ( { className, ...props } ) => {
|
const Block = ( { className } ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const { product } = productDataContext || props || {};
|
|
||||||
|
|
||||||
if ( isEmpty( product ) || isEmpty( product.tags ) ) {
|
if ( isEmpty( product.tags ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +36,9 @@ const Block = ( { className, ...props } ) => {
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-tag-list',
|
'wc-block-components-product-tag-list',
|
||||||
`${ parentClassName }__product-tag-list`
|
{
|
||||||
|
[ `${ parentClassName }__product-tag-list` ]: parentClassName,
|
||||||
|
}
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
{ __( 'Tags:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
{ __( 'Tags:', 'woo-gutenberg-products-block' ) }{ ' ' }
|
||||||
|
@ -59,7 +59,6 @@ const Block = ( { className, ...props } ) => {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { tag, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Tag List',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ tag } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display a list of tags belonging to a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Disabled } from '@wordpress/components';
|
||||||
|
import EditProductLink from '@woocommerce/block-components/edit-product-link';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
|
const Edit = ( { attributes } ) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EditProductLink />
|
||||||
|
<Disabled>
|
||||||
|
<Block { ...attributes } />
|
||||||
|
</Disabled>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's tags.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} )( Edit );
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerExperimentalBlockType( 'woocommerce/product-tag-list', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -1,4 +1,4 @@
|
||||||
.wc-block-layout .wc-block-components-product-category-list {
|
.wc-block-components-product-tag-list {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: em($gap-small);
|
margin-bottom: em($gap-small);
|
||||||
|
|
|
@ -12,6 +12,10 @@ let blockAttributes = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
productId: {
|
||||||
|
type: 'number',
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( isFeaturePluginBuild() ) {
|
if ( isFeaturePluginBuild() ) {
|
|
@ -11,6 +11,8 @@ import {
|
||||||
import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
|
import { getColorClassName, getFontSizeClass } from '@wordpress/block-editor';
|
||||||
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
import { isFeaturePluginBuild } from '@woocommerce/block-settings';
|
||||||
import { gatedStyledText } from '@woocommerce/atomic-utils';
|
import { gatedStyledText } from '@woocommerce/atomic-utils';
|
||||||
|
import { withProductDataContext } from '@woocommerce/shared-hocs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
|
@ -41,11 +43,9 @@ export const Block = ( {
|
||||||
customColor,
|
customColor,
|
||||||
fontSize,
|
fontSize,
|
||||||
customFontSize,
|
customFontSize,
|
||||||
...props
|
|
||||||
} ) => {
|
} ) => {
|
||||||
const { parentClassName } = useInnerBlockLayoutContext();
|
const { parentClassName } = useInnerBlockLayoutContext();
|
||||||
const productDataContext = useProductDataContext();
|
const { product } = useProductDataContext();
|
||||||
const product = props.product || productDataContext.product;
|
|
||||||
const TagName = `h${ headingLevel }`;
|
const TagName = `h${ headingLevel }`;
|
||||||
|
|
||||||
const colorClass = getColorClassName( 'color', color );
|
const colorClass = getColorClassName( 'color', color );
|
||||||
|
@ -58,19 +58,20 @@ export const Block = ( {
|
||||||
[ fontSizeClass ]: fontSizeClass,
|
[ fontSizeClass ]: fontSizeClass,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
if ( ! product ) {
|
if ( ! product.id ) {
|
||||||
return (
|
return (
|
||||||
<TagName
|
<TagName
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-title',
|
'wc-block-components-product-title',
|
||||||
`${ parentClassName }__product-title`,
|
|
||||||
{
|
{
|
||||||
|
[ `${ parentClassName }__product-title` ]: parentClassName,
|
||||||
[ `wc-block-components-product-title--align-${ align }` ]:
|
[ `wc-block-components-product-title--align-${ align }` ]:
|
||||||
align && isFeaturePluginBuild(),
|
align && isFeaturePluginBuild(),
|
||||||
|
[ titleClasses ]: isFeaturePluginBuild()
|
||||||
},
|
},
|
||||||
{ [ titleClasses ]: isFeaturePluginBuild() }
|
|
||||||
) }
|
) }
|
||||||
style={ gatedStyledText( {
|
style={ gatedStyledText( {
|
||||||
color: customColor,
|
color: customColor,
|
||||||
|
@ -88,11 +89,11 @@ export const Block = ( {
|
||||||
className={ classnames(
|
className={ classnames(
|
||||||
className,
|
className,
|
||||||
'wc-block-components-product-title',
|
'wc-block-components-product-title',
|
||||||
`${ parentClassName }__product-title`,
|
|
||||||
{
|
{
|
||||||
|
[ `${ parentClassName }__product-title` ]: parentClassName,
|
||||||
[ `wc-block-components-product-title__align-${ align }` ]:
|
[ `wc-block-components-product-title__align-${ align }` ]:
|
||||||
align && isFeaturePluginBuild(),
|
align && isFeaturePluginBuild(),
|
||||||
}
|
},
|
||||||
) }
|
) }
|
||||||
>
|
>
|
||||||
{ productLink ? (
|
{ productLink ? (
|
||||||
|
@ -118,7 +119,6 @@ export const Block = ( {
|
||||||
|
|
||||||
Block.propTypes = {
|
Block.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
product: PropTypes.object,
|
|
||||||
headingLevel: PropTypes.number,
|
headingLevel: PropTypes.number,
|
||||||
productLink: PropTypes.bool,
|
productLink: PropTypes.bool,
|
||||||
align: PropTypes.string,
|
align: PropTypes.string,
|
||||||
|
@ -128,4 +128,4 @@ Block.propTypes = {
|
||||||
customFontSize: PropTypes.number,
|
customFontSize: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Block;
|
export default withProductDataContext( Block );
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { bookmark, Icon } from '@woocommerce/icons';
|
||||||
|
|
||||||
|
export const BLOCK_TITLE = __(
|
||||||
|
'Product Title',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
||||||
|
export const BLOCK_ICON = <Icon srcElement={ bookmark } />;
|
||||||
|
export const BLOCK_DESCRIPTION = __(
|
||||||
|
'Display the title of a product.',
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
);
|
|
@ -20,6 +20,8 @@ import HeadingToolbar from '@woocommerce/block-components/heading-toolbar';
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import Block from './block';
|
import Block from './block';
|
||||||
|
import withProductSelector from '../shared/with-product-selector';
|
||||||
|
import { BLOCK_TITLE, BLOCK_ICON } from './constants';
|
||||||
|
|
||||||
const TitleEdit = ( {
|
const TitleEdit = ( {
|
||||||
color,
|
color,
|
||||||
|
@ -115,6 +117,14 @@ const Title = isFeaturePluginBuild()
|
||||||
? compose( [
|
? compose( [
|
||||||
withFontSizes( 'fontSize' ),
|
withFontSizes( 'fontSize' ),
|
||||||
withColors( 'color', { textColor: 'color' } ),
|
withColors( 'color', { textColor: 'color' } ),
|
||||||
|
withProductSelector( {
|
||||||
|
icon: BLOCK_ICON,
|
||||||
|
label: BLOCK_TITLE,
|
||||||
|
description: __(
|
||||||
|
"Choose a product to display it's title.",
|
||||||
|
'woo-gutenberg-products-block'
|
||||||
|
),
|
||||||
|
} ),
|
||||||
] )( TitleEdit )
|
] )( TitleEdit )
|
||||||
: TitleEdit;
|
: TitleEdit;
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { registerBlockType } from '@wordpress/blocks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import sharedConfig from '../shared/config';
|
||||||
|
import attributes from './attributes';
|
||||||
|
import edit from './edit';
|
||||||
|
import {
|
||||||
|
BLOCK_TITLE as title,
|
||||||
|
BLOCK_ICON as icon,
|
||||||
|
BLOCK_DESCRIPTION as description,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
const blockConfig = {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
icon: {
|
||||||
|
src: icon,
|
||||||
|
foreground: '#874FB9',
|
||||||
|
},
|
||||||
|
attributes,
|
||||||
|
edit,
|
||||||
|
};
|
||||||
|
|
||||||
|
registerBlockType( 'woocommerce/product-title', {
|
||||||
|
...sharedConfig,
|
||||||
|
...blockConfig,
|
||||||
|
} );
|
|
@ -0,0 +1,35 @@
|
||||||
|
.wc-block-components-product-title {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: $gap-small;
|
||||||
|
}
|
||||||
|
.wc-block-grid .wc-block-components-product-title {
|
||||||
|
line-height: 1.5;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0;
|
||||||
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.is-loading {
|
||||||
|
.wc-block-components-product-title::before {
|
||||||
|
@include placeholder();
|
||||||
|
content: ".";
|
||||||
|
display: inline-block;
|
||||||
|
width: 7em;
|
||||||
|
}
|
||||||
|
.wc-block-grid .wc-block-components-product-title::before {
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*rtl:begin:ignore*/
|
||||||
|
.wc-block-components-product-title--align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.wc-block-components-product-title--align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wc-block-components-product-title--align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
/*rtl:end:ignore*/
|
|
@ -1,32 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { __ } from '@wordpress/i18n';
|
|
||||||
import { registerExperimentalBlockType } from '@woocommerce/block-settings';
|
|
||||||
import { Icon, cart } from '@woocommerce/icons';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import sharedConfig from '../shared-config';
|
|
||||||
import edit from './edit';
|
|
||||||
import attributes from './attributes';
|
|
||||||
|
|
||||||
const blockConfig = {
|
|
||||||
title: __( 'Add to Cart', 'woo-gutenberg-products-block' ),
|
|
||||||
description: __(
|
|
||||||
'Displays an add to cart button. Optionally displays other add to cart form elements.',
|
|
||||||
'woo-gutenberg-products-block'
|
|
||||||
),
|
|
||||||
icon: {
|
|
||||||
src: <Icon srcElement={ cart } />,
|
|
||||||
foreground: '#96588a',
|
|
||||||
},
|
|
||||||
edit,
|
|
||||||
attributes,
|
|
||||||
};
|
|
||||||
|
|
||||||
registerExperimentalBlockType( 'woocommerce/product-add-to-cart', {
|
|
||||||
...sharedConfig,
|
|
||||||
...blockConfig,
|
|
||||||
} );
|
|
|
@ -1,45 +0,0 @@
|
||||||
.wc-block-layout {
|
|
||||||
.wc-block-components-product-add-to-cart {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.wc-block-components-product-add-to-cart-button {
|
|
||||||
margin: 0 0 em($gap-small) 0;
|
|
||||||
|
|
||||||
.wc-block-components-button__text {
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
> svg {
|
|
||||||
fill: currentColor;
|
|
||||||
vertical-align: top;
|
|
||||||
width: 1.5em;
|
|
||||||
height: 1.5em;
|
|
||||||
margin: -0.25em 0 -0.25em 0.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wc-block-components-product-add-to-cart-quantity {
|
|
||||||
margin: 0 1em em($gap-small) 0;
|
|
||||||
width: 5em;
|
|
||||||
padding: 0.618em;
|
|
||||||
background: $white;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 2px;
|
|
||||||
color: #43454b;
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.125);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.wc-block-components-product-add-to-cart--placeholder {
|
|
||||||
.wc-block-components-product-add-to-cart-quantity,
|
|
||||||
.wc-block-components-product-add-to-cart-button {
|
|
||||||
@include placeholder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.wc-block-grid .wc-block-components-product-add-to-cart {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { Disabled } from '@wordpress/components';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import Block from './block';
|
|
||||||
|
|
||||||
export default ( { attributes } ) => {
|
|
||||||
return (
|
|
||||||
<Disabled>
|
|
||||||
<Block { ...attributes } />
|
|
||||||
</Disabled>
|
|
||||||
);
|
|
||||||
};
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue