rebase from live enviornment

This commit is contained in:
Rachit Bhargava
2024-01-09 22:14:20 -05:00
parent ff0b49a046
commit 3a22fcaa4a
15968 changed files with 2344674 additions and 45234 deletions

View File

@@ -0,0 +1,56 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { getSetting } from '@woocommerce/settings';
import { Icon } from '@wordpress/icons';
import { filledCart, removeCart } from '@woocommerce/icons';
export const blockName = 'woocommerce/cart';
export const blockAttributes = {
isPreview: {
type: 'boolean',
default: false,
},
currentView: {
type: 'string',
default: 'woocommerce/filled-cart-block',
source: 'readonly', // custom source to prevent saving to post content
},
editorViews: {
type: 'object',
default: [
{
view: 'woocommerce/filled-cart-block',
label: __( 'Filled Cart', 'woocommerce' ),
icon: <Icon icon={ filledCart } />,
},
{
view: 'woocommerce/empty-cart-block',
label: __( 'Empty Cart', 'woocommerce' ),
icon: <Icon icon={ removeCart } />,
},
],
},
hasDarkControls: {
type: 'boolean',
default: getSetting( 'hasDarkEditorStyleSupport', false ),
},
// Deprecated - here for v1 migration support
isShippingCalculatorEnabled: {
type: 'boolean',
default: getSetting( 'isShippingCalculatorEnabled', true ),
},
checkoutPageId: {
type: 'number',
default: 0,
},
showRateAfterTaxName: {
type: 'boolean',
default: true,
},
align: {
type: 'string',
default: 'wide',
},
};

View File

@@ -0,0 +1,98 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useStoreCart } from '@woocommerce/base-context/hooks';
import { useEffect } from '@wordpress/element';
import LoadingMask from '@woocommerce/base-components/loading-mask';
import { CURRENT_USER_IS_ADMIN } from '@woocommerce/settings';
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
import { translateJQueryEventToNative } from '@woocommerce/base-utils';
import withScrollToTop from '@woocommerce/base-hocs/with-scroll-to-top';
import {
CartEventsProvider,
CartProvider,
noticeContexts,
} from '@woocommerce/base-context';
import { SlotFillProvider } from '@woocommerce/blocks-checkout';
import { StoreNoticesContainer } from '@woocommerce/blocks-components';
/**
* Internal dependencies
*/
import { CartBlockContext } from './context';
import './style.scss';
const reloadPage = () => void window.location.reload( true );
const Cart = ( { children, attributes = {} } ) => {
const { cartIsLoading } = useStoreCart();
const { hasDarkControls } = attributes;
return (
<LoadingMask showSpinner={ true } isLoading={ cartIsLoading }>
<CartBlockContext.Provider
value={ {
hasDarkControls,
} }
>
{ children }
</CartBlockContext.Provider>
</LoadingMask>
);
};
const ScrollOnError = ( { scrollToTop } ) => {
useEffect( () => {
// Make it so we can read jQuery events triggered by WC Core elements.
const removeJQueryAddedToCartEvent = translateJQueryEventToNative(
'added_to_cart',
'wc-blocks_added_to_cart'
);
document.body.addEventListener(
'wc-blocks_added_to_cart',
scrollToTop
);
return () => {
removeJQueryAddedToCartEvent();
document.body.removeEventListener(
'wc-blocks_added_to_cart',
scrollToTop
);
};
}, [ scrollToTop ] );
return null;
};
const Block = ( { attributes, children, scrollToTop } ) => (
<BlockErrorBoundary
header={ __(
'Something went wrong. Please contact us for assistance.',
'woocommerce'
) }
text={ __(
'The cart has encountered an unexpected error. If the error persists, please get in touch with us for help.',
'woocommerce'
) }
button={
<button className="wc-block-button" onClick={ reloadPage }>
{ __( 'Reload the page', 'woocommerce' ) }
</button>
}
showErrorMessage={ CURRENT_USER_IS_ADMIN }
>
<StoreNoticesContainer context={ noticeContexts.CART } />
<SlotFillProvider>
<CartProvider>
<CartEventsProvider>
<Cart attributes={ attributes }>{ children }</Cart>
<ScrollOnError scrollToTop={ scrollToTop } />
</CartEventsProvider>
</CartProvider>
</SlotFillProvider>
</BlockErrorBoundary>
);
export default withScrollToTop( Block );

View File

@@ -0,0 +1,69 @@
/**
* External dependencies
*/
import {
InnerBlockLayoutContextProvider,
ProductDataContextProvider,
} from '@woocommerce/shared-context';
import { ProductResponseItem } from '@woocommerce/types';
/**
* Internal dependencies
*/
import { Block as ProductImage } from '../../../atomic/blocks/product-elements/image/block';
import { Block as ProductName } from '../../../atomic/blocks/product-elements/title/block';
import { Block as ProductRating } from '../../../atomic/blocks/product-elements/rating/block';
import { Block as ProductSaleBadge } from '../../../atomic/blocks/product-elements/sale-badge/block';
import { Block as ProductPrice } from '../../../atomic/blocks/product-elements/price/block';
import { Block as ProductButton } from '../../../atomic/blocks/product-elements/button/block';
import { ImageSizing } from '../../../atomic/blocks/product-elements/image/types';
interface CrossSellsProductProps {
product: ProductResponseItem;
isLoading: boolean;
}
const CartCrossSellsProduct = ( {
product,
}: CrossSellsProductProps ): JSX.Element => {
return (
<div className="cross-sells-product">
<InnerBlockLayoutContextProvider
parentName={ 'woocommerce/cart-cross-sells-block' }
parentClassName={ 'wp-block-cart-cross-sells-product' }
>
<ProductDataContextProvider
// Setting isLoading to false, given this parameter is required.
isLoading={ false }
product={ product }
>
<div>
<ProductImage
className={ '' }
showSaleBadge={ false }
productId={ product.id }
showProductLink={ false }
saleBadgeAlign={ 'left' }
imageSizing={ ImageSizing.SINGLE }
isDescendentOfQueryLoop={ false }
/>
<ProductName
align={ '' }
headingLevel={ 3 }
showProductLink={ true }
/>
<ProductRating />
<ProductSaleBadge
productId={ product.id }
align={ 'left' }
/>
<ProductPrice />
</div>
<ProductButton />
</ProductDataContextProvider>
</InnerBlockLayoutContextProvider>
</div>
);
};
export default CartCrossSellsProduct;

View File

@@ -0,0 +1,37 @@
/**
* External dependencies
*/
import { ProductResponseItem } from '@woocommerce/types';
/**
* Internal dependencies
*/
import CartCrossSellsProduct from './cart-cross-sells-product';
interface CrossSellsProductListProps {
products: ProductResponseItem[];
className?: string | undefined;
columns: number;
}
const CartCrossSellsProductList = ( {
products,
columns,
}: CrossSellsProductListProps ): JSX.Element => {
const crossSellsProducts = products.map( ( product, i ) => {
if ( i >= columns ) return null;
return (
<CartCrossSellsProduct
// Setting isLoading to false, given this parameter is required.
isLoading={ false }
product={ product }
key={ product.id }
/>
);
} );
return <div>{ crossSellsProducts }</div>;
};
export default CartCrossSellsProductList;

View File

@@ -0,0 +1,19 @@
/**
* External dependencies
*/
import { createContext, useContext } from '@wordpress/element';
/**
* Context consumed by inner blocks.
*/
export type CartBlockContextProps = {
hasDarkControls: boolean;
};
export const CartBlockContext = createContext< CartBlockContextProps >( {
hasDarkControls: false,
} );
export const useCartBlockContext = (): CartBlockContextProps => {
return useContext( CartBlockContext );
};

View File

@@ -0,0 +1,130 @@
/* tslint:disable */
/**
* External dependencies
*/
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
InnerBlocks,
InspectorControls,
} from '@wordpress/block-editor';
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
import { EditorProvider, CartProvider } from '@woocommerce/base-context';
import { previewCart } from '@woocommerce/resource-previews';
import { SlotFillProvider } from '@woocommerce/blocks-checkout';
import { useEffect, useRef } from '@wordpress/element';
import { getQueryArg } from '@wordpress/url';
import { dispatch, select } from '@wordpress/data';
/**
* Internal dependencies
*/
import './inner-blocks';
import './editor.scss';
import {
addClassToBody,
useBlockPropsWithLocking,
BlockSettings,
} from '../cart-checkout-shared';
import '../cart-checkout-shared/sidebar-notices';
import '../cart-checkout-shared/view-switcher';
import { CartBlockContext } from './context';
// This is adds a class to body to signal if the selected block is locked
addClassToBody();
// Array of allowed block names.
const ALLOWED_BLOCKS = [
'woocommerce/filled-cart-block',
'woocommerce/empty-cart-block',
];
export const Edit = ( { clientId, className, attributes, setAttributes } ) => {
const { hasDarkControls, currentView, isPreview = false } = attributes;
const defaultTemplate = [
[ 'woocommerce/filled-cart-block', {}, [] ],
[ 'woocommerce/empty-cart-block', {}, [] ],
];
const blockProps = useBlockPropsWithLocking( {
className: classnames( className, 'wp-block-woocommerce-cart', {
'is-editor-preview': isPreview,
} ),
} );
// This focuses on the block when a certain query param is found. This is used on the link from the task list.
const focus = useRef( getQueryArg( window.location.href, 'focus' ) );
useEffect( () => {
if (
focus.current === 'cart' &&
! select( 'core/block-editor' ).hasSelectedBlock()
) {
dispatch( 'core/block-editor' ).selectBlock( clientId );
dispatch( 'core/interface' ).enableComplementaryArea(
'core/edit-site',
'edit-site/block-inspector'
);
}
}, [ clientId ] );
return (
<div { ...blockProps }>
<InspectorControls>
<BlockSettings
attributes={ attributes }
setAttributes={ setAttributes }
/>
</InspectorControls>
<BlockErrorBoundary
header={ __(
'Cart Block Error',
'woocommerce'
) }
text={ __(
'There was an error whilst rendering the cart block. If this problem continues, try re-creating the block.',
'woocommerce'
) }
showErrorMessage={ true }
errorMessagePrefix={ __(
'Error message:',
'woocommerce'
) }
>
<EditorProvider
previewData={ { previewCart } }
currentView={ currentView }
isPreview={ isPreview }
>
<CartBlockContext.Provider
value={ {
hasDarkControls,
} }
>
<SlotFillProvider>
<CartProvider>
<InnerBlocks
allowedBlocks={ ALLOWED_BLOCKS }
template={ defaultTemplate }
templateLock="insert"
/>
</CartProvider>
</SlotFillProvider>
</CartBlockContext.Provider>
</EditorProvider>
</BlockErrorBoundary>
</div>
);
};
export const Save = () => {
return (
<div
{ ...useBlockProps.save( {
className: 'is-loading',
} ) }
>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,37 @@
body.wc-lock-selected-block--move {
.block-editor-block-mover__move-button-container,
.block-editor-block-mover {
display: none;
}
}
body.wc-lock-selected-block--remove {
.block-editor-block-settings-menu__popover {
.components-menu-group:last-child {
display: none;
}
.components-menu-group:nth-last-child(2) {
margin-bottom: -12px;
}
}
}
.wp-block-woocommerce-cart-items-block,
.wp-block-woocommerce-cart-totals-block,
.wp-block-woocommerce-empty-cart-block {
// Temporary fix after the appender button was positioned absolute
// See https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5742#issuecomment-1032804168
.block-list-appender {
position: relative;
}
}
.wp-block-woocommerce-cart-order-summary-block {
.block-editor-block-list__layout > div {
margin: 0 !important;
}
.wc-block-components-totals-wrapper {
box-sizing: border-box;
}
}

View File

@@ -0,0 +1,50 @@
/**
* External dependencies
*/
import { getValidBlockAttributes } from '@woocommerce/base-utils';
import { Children, cloneElement, isValidElement } from '@wordpress/element';
import { useStoreCart } from '@woocommerce/base-context';
import { getRegisteredBlockComponents } from '@woocommerce/blocks-registry';
import { renderParentBlock } from '@woocommerce/atomic-utils';
/**
* Internal dependencies
*/
import './inner-blocks/register-components';
import Block from './block';
import { blockName, blockAttributes } from './attributes';
const getProps = ( el ) => {
return {
attributes: getValidBlockAttributes(
blockAttributes,
!! el ? el.dataset : {}
),
};
};
const Wrapper = ( { children } ) => {
// we need to pluck out receiveCart.
// eslint-disable-next-line no-unused-vars
const { extensions, receiveCart, ...cart } = useStoreCart();
return Children.map( children, ( child ) => {
if ( isValidElement( child ) ) {
const componentProps = {
extensions,
cart,
};
return cloneElement( child, componentProps );
}
return child;
} );
};
renderParentBlock( {
Block,
blockName,
selector: '.wp-block-woocommerce-cart',
getProps,
blockMap: getRegisteredBlockComponents( blockName ),
blockWrapper: Wrapper,
} );

View File

@@ -0,0 +1,131 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import { InnerBlocks } from '@wordpress/block-editor';
import { cart } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType, createBlock } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import './style.scss';
import { blockName, blockAttributes } from './attributes';
import './inner-blocks';
/**
* Register and run the Cart block.
*/
const settings = {
title: __( 'Cart', 'woocommerce' ),
icon: {
src: (
<Icon
icon={ cart }
className="wc-block-editor-components-block-icon"
/>
),
},
category: 'woocommerce',
keywords: [ __( 'WooCommerce', 'woocommerce' ) ],
description: __( 'Shopping cart.', 'woocommerce' ),
supports: {
align: [ 'wide' ],
html: false,
multiple: false,
},
example: {
attributes: {
isPreview: true,
},
viewportWidth: 800,
},
attributes: blockAttributes,
edit: Edit,
save: Save,
transforms: {
to: [
{
type: 'block',
blocks: [ 'woocommerce/classic-shortcode' ],
transform: ( attributes ) => {
return createBlock(
'woocommerce/classic-shortcode',
{
shortcode: 'cart',
align: attributes.align,
},
[]
);
},
},
],
},
// Migrates v1 to v2 checkout.
deprecated: [
{
attributes: blockAttributes,
save: ( { attributes } ) => {
return (
<div
className={ classnames(
'is-loading',
attributes.className
) }
>
<InnerBlocks.Content />
</div>
);
},
migrate: ( attributes, innerBlocks ) => {
const { checkoutPageId, align } = attributes;
return [
attributes,
[
createBlock(
'woocommerce/filled-cart-block',
{ align },
[
createBlock( 'woocommerce/cart-items-block' ),
createBlock(
'woocommerce/cart-totals-block',
{},
[
createBlock(
'woocommerce/cart-order-summary-block',
{}
),
createBlock(
'woocommerce/cart-express-payment-block'
),
createBlock(
'woocommerce/proceed-to-checkout-block',
{ checkoutPageId }
),
createBlock(
'woocommerce/cart-accepted-payment-methods-block'
),
]
),
]
),
createBlock(
'woocommerce/empty-cart-block',
{ align },
innerBlocks
),
],
];
},
isEligible: ( _, innerBlocks ) => {
return ! innerBlocks.find(
( block ) => block.name === 'woocommerce/filled-cart-block'
);
},
},
],
};
registerBlockType( blockName, settings );

View File

@@ -0,0 +1,18 @@
{
"name": "woocommerce/cart-accepted-payment-methods-block",
"version": "1.0.0",
"title": "Accepted Payment Methods",
"description": "Display accepted payment methods.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": true
},
"parent": [ "woocommerce/cart-totals-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,19 @@
/**
* External dependencies
*/
import { PaymentMethodIcons } from '@woocommerce/base-components/cart-checkout';
import { usePaymentMethods } from '@woocommerce/base-context/hooks';
import { getIconsFromPaymentMethods } from '@woocommerce/base-utils';
const Block = ( { className }: { className: string } ): JSX.Element => {
const { paymentMethods } = usePaymentMethods();
return (
<PaymentMethodIcons
className={ className }
icons={ getIconsFromPaymentMethods( paymentMethods ) }
/>
);
};
export default Block;

View File

@@ -0,0 +1,27 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: { className: string };
} ): JSX.Element => {
const { className } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Block className={ className } />
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,23 @@
/**
* External dependencies
*/
import { registerBlockType } from '@wordpress/blocks';
import { Icon, payment } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-accepted-payment-methods-block', {
icon: {
src: (
<Icon
icon={ payment }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,18 @@
{
"name": "woocommerce/cart-cross-sells-block",
"version": "1.0.0",
"title": "Cart Cross-Sells",
"description": "Shows the Cross-Sells block.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": true
},
"parent": [ "woocommerce/cart-items-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,41 @@
/**
* External dependencies
*/
import type { TemplateArray } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
export const Edit = (): JSX.Element => {
const blockProps = useBlockProps( {
className: 'wc-block-cart__cross-sells',
} );
const defaultTemplate = [
[
'core/heading',
{
content: __(
'You may be interested in…',
'woo-gutenberg-products-block'
),
level: 2,
fontSize: 'large',
},
[],
],
[ 'woocommerce/cart-cross-sells-products-block', {}, [] ],
] as TemplateArray;
return (
<div { ...blockProps }>
<InnerBlocks template={ defaultTemplate } templateLock={ false } />
</div>
);
};
export const Save = (): JSX.Element => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { useStoreCart } from '@woocommerce/base-context/hooks';
interface Props {
children?: JSX.Element | JSX.Element[];
className?: string;
}
const FrontendBlock = ( {
children,
className = '',
}: Props ): JSX.Element | null => {
const { crossSellsProducts, cartIsLoading } = useStoreCart();
if ( cartIsLoading || crossSellsProducts.length < 1 ) {
return null;
}
return <div className={ className }>{ children }</div>;
};
export default FrontendBlock;

View File

@@ -0,0 +1,23 @@
/**
* External dependencies
*/
import { Icon, column } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-cross-sells-block', {
icon: {
src: (
<Icon
icon={ column }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,32 @@
{
"name": "woocommerce/cart-cross-sells-products-block",
"version": "1.0.0",
"title": "Cart Cross-Sells Products",
"description": "Shows the Cross-Sells products.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"columns": {
"type": "number",
"default": 3
},
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/cart-cross-sells-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { useStoreCart } from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import CartCrossSellsProductList from '../../cart-cross-sells-product-list';
import metadata from './block.json';
interface BlockProps {
className?: string | undefined;
columns: number;
}
const Block = ( { className, columns }: BlockProps ): JSX.Element => {
const { crossSellsProducts } = useStoreCart();
if ( typeof columns === 'undefined' ) {
columns = metadata.attributes.columns.default;
}
return (
<CartCrossSellsProductList
className={ className }
columns={ columns }
products={ crossSellsProducts }
/>
);
};
export default Block;

View File

@@ -0,0 +1,59 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { PanelBody, RangeControl } from '@wordpress/components';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { getSetting } from '@woocommerce/settings';
import Noninteractive from '@woocommerce/base-components/noninteractive';
/**
* Internal dependencies
*/
import Block from './block';
import './editor.scss';
interface Attributes {
className?: string;
columns: number;
}
interface Props {
attributes: Attributes;
setAttributes: ( attributes: Record< string, unknown > ) => void;
}
export const Edit = ( { attributes, setAttributes }: Props ): JSX.Element => {
const { className, columns } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<InspectorControls>
<PanelBody
title={ __( 'Settings', 'woo-gutenberg-products-block' ) }
>
<RangeControl
label={ __(
'Cross-Sells products to show',
'woo-gutenberg-products-block'
) }
value={ columns }
onChange={ ( value ) =>
setAttributes( { columns: value } )
}
min={ getSetting( 'minColumns', 1 ) }
max={ getSetting( 'maxColumns', 6 ) }
/>
</PanelBody>
</InspectorControls>
<Noninteractive>
<Block columns={ columns } className={ className } />
</Noninteractive>
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,38 @@
.wp-block-woocommerce-cart-cross-sells-products-block {
.cross-sells-product {
display: inline-block;
margin-bottom: 2em;
padding-right: 5%;
text-align: center;
vertical-align: top;
width: 30%;
&:nth-child(3n + 3) {
padding-right: 0;
}
div {
.wc-block-components-product-name {
font-weight: 400;
}
.wc-block-components-product-price {
display: block;
}
}
.wc-block-components-product-add-to-cart-button:not(.is-link) {
background-color: #eee;
color: #333;
margin-top: 1em;
&:focus,
&:hover {
background-color: #d5d5d5;
border-color: #d5d5d5;
color: #333;
}
}
}
}

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { Icon, column } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import './style.scss';
registerBlockType( 'woocommerce/cart-cross-sells-products-block', {
icon: {
src: (
<Icon
icon={ column }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,72 @@
.wp-block-woocommerce-cart {
&.is-loading .wp-block-woocommerce-cart-cross-sells-block {
@include placeholder();
margin-top: 2em;
min-height: 15em;
h3 {
display: none;
}
}
.wp-block-woocommerce-cart-cross-sells-block {
.cross-sells-product {
display: inline-block;
box-sizing: content-box;
margin-bottom: 2em;
padding-right: 5%;
text-align: center;
vertical-align: top;
width: 30%;
&:nth-child(3n + 3) {
padding-right: 0;
}
div {
.wc-block-components-product-name {
font-weight: 400;
}
.wc-block-components-product-price {
display: block;
}
}
.wc-block-components-product-button__button {
margin-top: 1em;
}
.wc-block-components-product-add-to-cart {
justify-content: center;
.wc-block-components-product-add-to-cart-button:not(.is-link) {
background-color: #eee;
color: #333;
font-weight: 600;
margin-top: 1em;
&:focus,
&:hover {
background-color: #d5d5d5;
border-color: #d5d5d5;
color: #333;
}
}
}
}
}
}
@include breakpoint("<480px") {
.wp-block-woocommerce-cart {
.wp-block-woocommerce-cart-cross-sells-block {
.cross-sells-product {
display: block;
width: 100%;
}
}
}
}

View File

@@ -0,0 +1,28 @@
{
"name": "woocommerce/cart-express-payment-block",
"version": "1.0.0",
"title": "Express Checkout",
"description": "Allow customers to breeze through with quick payment options.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/cart-totals-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { useStoreCart } from '@woocommerce/base-context/hooks';
import classnames from 'classnames';
/**
* Internal dependencies
*/
import { CartExpressPayment } from '../../../cart-checkout-shared/payment-methods';
const Block = ( { className }: { className: string } ): JSX.Element | null => {
const { cartNeedsPayment } = useStoreCart();
if ( ! cartNeedsPayment ) {
return null;
}
return (
<div
className={ classnames(
'wc-block-cart__payment-options',
className
) }
>
<CartExpressPayment />
</div>
);
};
export default Block;

View File

@@ -0,0 +1,42 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
import { useExpressPaymentMethods } from '@woocommerce/base-context/hooks';
import classnames from 'classnames';
/**
* Internal dependencies
*/
import Block from './block';
import './editor.scss';
export const Edit = ( {
attributes,
}: {
attributes: { className: string };
} ): JSX.Element | null => {
const { paymentMethods, isInitialized } = useExpressPaymentMethods();
const hasExpressPaymentMethods = Object.keys( paymentMethods ).length > 0;
const blockProps = useBlockProps( {
className: classnames( {
'wp-block-woocommerce-cart-express-payment-block--has-express-payment-methods':
hasExpressPaymentMethods,
} ),
} );
const { className } = attributes;
if ( ! isInitialized || ! hasExpressPaymentMethods ) {
return null;
}
return (
<div { ...blockProps }>
<Block className={ className } />
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,34 @@
// Adjust padding and margins in the editor to improve selected block outlines.
.wp-block-woocommerce-cart-express-payment-block {
.components-placeholder__label svg {
font-size: 1em;
}
.wc-block-cart__payment-options {
padding: 0;
.wc-block-components-express-payment-continue-rule {
margin-bottom: -12px;
}
}
}
.wp-block-woocommerce-proceed-to-checkout-block {
margin-bottom: 28px;
margin-top: 28px;
}
.wp-block-woocommerce-checkout-express-payment-block-placeholder {
* {
pointer-events: all; // Overrides parent disabled component in editor context
}
.wp-block-woocommerce-cart-express-payment-block &,
.wp-block-woocommerce-checkout-express-payment-block-placeholder__description {
margin: 0 0 1em;
}
.wp-block-woocommerce-checkout-express-payment-block-placeholder__description {
display: block;
}
}

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,25 @@
/**
* External dependencies
*/
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import expressIcon from '../../../cart-checkout-shared/icon';
registerBlockType( 'woocommerce/cart-express-payment-block', {
icon: {
src: (
<Icon
style={ { fill: 'none' } } // this is needed for this particular svg
icon={ expressIcon }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,28 @@
{
"name": "woocommerce/cart-items-block",
"version": "1.0.0",
"title": "Cart Items",
"description": "Column containing cart items.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/filled-cart-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,51 @@
/**
* External dependencies
*/
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { Main } from '@woocommerce/base-components/sidebar-layout';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import type { TemplateArray } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import {
useForcedLayout,
getAllowedBlocks,
} from '../../../cart-checkout-shared';
interface Props {
clientId: string;
}
export const Edit = ( { clientId }: Props ): JSX.Element => {
const blockProps = useBlockProps( { className: 'wc-block-cart__main' } );
const allowedBlocks = getAllowedBlocks( innerBlockAreas.CART_ITEMS );
const defaultTemplate = [
[ 'woocommerce/cart-line-items-block', {}, [] ],
[ 'woocommerce/cart-cross-sells-block', {}, [] ],
] as unknown as TemplateArray;
useForcedLayout( {
clientId,
registeredBlocks: allowedBlocks,
defaultTemplate,
} );
return (
<Main { ...blockProps }>
<InnerBlocks
allowedBlocks={ allowedBlocks }
template={ defaultTemplate }
templateLock={ false }
renderAppender={ InnerBlocks.ButtonBlockAppender }
/>
</Main>
);
};
export const Save = (): JSX.Element => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,21 @@
/**
* External dependencies
*/
import { Main } from '@woocommerce/base-components/sidebar-layout';
import classnames from 'classnames';
const FrontendBlock = ( {
children,
className,
}: {
children: JSX.Element;
className: string;
} ): JSX.Element => {
return (
<Main className={ classnames( 'wc-block-cart__main', className ) }>
{ children }
</Main>
);
};
export default FrontendBlock;

View File

@@ -0,0 +1,23 @@
/**
* External dependencies
*/
import { Icon, column } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-items-block', {
icon: {
src: (
<Icon
icon={ column }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,28 @@
{
"name": "woocommerce/cart-line-items-block",
"version": "1.0.0",
"title": "Cart Line Items",
"description": "Block containing current line items in Cart.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/cart-items-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import { useStoreCart } from '@woocommerce/base-context/hooks';
import { CartLineItemsTable } from '@woocommerce/base-components/cart-checkout';
const Block = ( { className }: { className: string } ): JSX.Element => {
const { cartItems, cartIsLoading } = useStoreCart();
return (
<CartLineItemsTable
className={ className }
lineItems={ cartItems }
isLoading={ cartIsLoading }
/>
);
};
export default Block;

View File

@@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
import Noninteractive from '@woocommerce/base-components/noninteractive';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: { className: string };
} ): JSX.Element => {
const { className } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Noninteractive>
<Block className={ className } />
</Noninteractive>
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,23 @@
/**
* External dependencies
*/
import { Icon, column } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-line-items-block', {
icon: {
src: (
<Icon
icon={ column }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,28 @@
{
"name": "woocommerce/cart-order-summary-block",
"version": "1.0.0",
"title": "Order Summary",
"description": "Show customers a summary of their order.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/cart-totals-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,74 @@
/**
* External dependencies
*/
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import type { TemplateArray } from '@wordpress/blocks';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { __ } from '@wordpress/i18n';
import { TotalsFooterItem } from '@woocommerce/base-components/cart-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import {
useForcedLayout,
getAllowedBlocks,
} from '../../../cart-checkout-shared';
import { OrderMetaSlotFill } from './slotfills';
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
const blockProps = useBlockProps();
const { cartTotals } = useStoreCart();
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
const allowedBlocks = getAllowedBlocks(
innerBlockAreas.CART_ORDER_SUMMARY
);
const defaultTemplate = [
[
'woocommerce/cart-order-summary-heading-block',
{
content: __( 'Cart totals', 'woo-gutenberg-products-block' ),
},
[],
],
[ 'woocommerce/cart-order-summary-coupon-form-block', {}, [] ],
[ 'woocommerce/cart-order-summary-subtotal-block', {}, [] ],
[ 'woocommerce/cart-order-summary-fee-block', {}, [] ],
[ 'woocommerce/cart-order-summary-discount-block', {}, [] ],
[ 'woocommerce/cart-order-summary-shipping-block', {}, [] ],
[ 'woocommerce/cart-order-summary-taxes-block', {}, [] ],
] as TemplateArray;
useForcedLayout( {
clientId,
registeredBlocks: allowedBlocks,
defaultTemplate,
} );
return (
<div { ...blockProps }>
<InnerBlocks
allowedBlocks={ allowedBlocks }
template={ defaultTemplate }
/>
<div className="wc-block-components-totals-wrapper">
<TotalsFooterItem
currency={ totalsCurrency }
values={ cartTotals }
/>
</div>
{ /* do I put an totals wrapper here? */ }
<OrderMetaSlotFill />
</div>
);
};
export const Save = (): JSX.Element => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,37 @@
/**
* External dependencies
*/
import { TotalsFooterItem } from '@woocommerce/base-components/cart-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import { OrderMetaSlotFill } from './slotfills';
const FrontendBlock = ( {
children,
className = '',
}: {
children?: JSX.Element | JSX.Element[];
className?: string;
} ): JSX.Element | null => {
const { cartTotals } = useStoreCart();
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<div className={ className }>
{ children }
<div className="wc-block-components-totals-wrapper">
<TotalsFooterItem
currency={ totalsCurrency }
values={ cartTotals }
/>
</div>
<OrderMetaSlotFill />
</div>
);
};
export default FrontendBlock;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import { ExperimentalOrderMeta } from '@woocommerce/blocks-checkout';
import { useStoreCart } from '@woocommerce/base-context/hooks';
export const OrderMetaSlotFill = (): JSX.Element => {
// Prepare props to pass to the ExperimentalOrderMeta slot fill. We need to pluck out receiveCart.
// eslint-disable-next-line no-unused-vars
const { extensions, receiveCart, ...cart } = useStoreCart();
const slotFillProps = {
extensions,
cart,
context: 'woocommerce/cart',
};
return <ExperimentalOrderMeta.Slot { ...slotFillProps } />;
};

View File

@@ -0,0 +1,30 @@
{
"name": "woocommerce/cart-order-summary-coupon-form-block",
"version": "1.0.0",
"title": "Coupon Form",
"description": "Shows the apply coupon form.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"lock": {
"type": "object",
"default": {
"remove": false,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { TotalsCoupon } from '@woocommerce/base-components/cart-checkout';
import { useStoreCartCoupons } from '@woocommerce/base-context/hooks';
import { getSetting } from '@woocommerce/settings';
import { TotalsWrapper } from '@woocommerce/blocks-components';
const Block = ( { className }: { className: string } ): JSX.Element | null => {
const couponsEnabled = getSetting( 'couponsEnabled', true );
const { applyCoupon, isApplyingCoupon } = useStoreCartCoupons( 'wc/cart' );
if ( ! couponsEnabled ) {
return null;
}
return (
<TotalsWrapper className={ className }>
<TotalsCoupon
onSubmit={ applyCoupon }
isLoading={ isApplyingCoupon }
/>
</TotalsWrapper>
);
};
export default Block;

View File

@@ -0,0 +1,33 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
import Noninteractive from '@woocommerce/base-components/noninteractive';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: {
className: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { className } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Noninteractive>
<Block className={ className } />
</Noninteractive>
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,23 @@
/**
* External dependencies
*/
import { Icon, tag } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-coupon-form-block', {
icon: {
src: (
<Icon
icon={ tag }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,31 @@
{
"name": "woocommerce/cart-order-summary-discount-block",
"version": "1.0.0",
"title": "Discount",
"description": "Shows the cart discount row.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"lock": false
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"lock": {
"type": "object",
"default": {
"remove": true,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,47 @@
/**
* External dependencies
*/
import { TotalsDiscount } from '@woocommerce/base-components/cart-checkout';
import { TotalsWrapper } from '@woocommerce/blocks-components';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import {
useStoreCartCoupons,
useStoreCart,
} from '@woocommerce/base-context/hooks';
import { ExperimentalDiscountsMeta } from '@woocommerce/blocks-checkout';
const DiscountSlotFill = (): JSX.Element => {
// Prepare props to pass to the ExperimentalOrderMeta slot fill. We need to pluck out receiveCart.
// eslint-disable-next-line no-unused-vars
const { extensions, receiveCart, ...cart } = useStoreCart();
const discountsSlotFillProps = {
extensions,
cart,
context: 'woocommerce/cart',
};
return <ExperimentalDiscountsMeta.Slot { ...discountsSlotFillProps } />;
};
const Block = ( { className }: { className: string } ): JSX.Element => {
const { cartTotals, cartCoupons } = useStoreCart();
const { removeCoupon, isRemovingCoupon } = useStoreCartCoupons( 'wc/cart' );
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<>
<TotalsWrapper className={ className }>
<TotalsDiscount
cartCoupons={ cartCoupons }
currency={ totalsCurrency }
isRemovingCoupon={ isRemovingCoupon }
removeCoupon={ removeCoupon }
values={ cartTotals }
/>
</TotalsWrapper>
<DiscountSlotFill />
</>
);
};
export default Block;

View File

@@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: {
className: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { className } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Block className={ className } />
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-discount-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,31 @@
{
"name": "woocommerce/cart-order-summary-fee-block",
"version": "1.0.0",
"title": "Fees",
"description": "Shows the cart fee row.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"lock": false
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"lock": {
"type": "object",
"default": {
"remove": true,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,19 @@
/**
* External dependencies
*/
import { TotalsFees, TotalsWrapper } from '@woocommerce/blocks-components';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks';
const Block = ( { className }: { className: string } ): JSX.Element => {
const { cartFees, cartTotals } = useStoreCart();
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<TotalsWrapper className={ className }>
<TotalsFees currency={ totalsCurrency } cartFees={ cartFees } />
</TotalsWrapper>
);
};
export default Block;

View File

@@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: {
className: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { className } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Block className={ className } />
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-fee-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
export default {
content: {
type: 'string',
default: __( 'Cart totals', 'woo-gutenberg-products-block' ),
},
lock: {
type: 'object',
default: {
remove: false,
move: false,
},
},
};

View File

@@ -0,0 +1,34 @@
{
"name": "woocommerce/cart-order-summary-heading-block",
"version": "1.0.0",
"title": "Heading",
"description": "Shows the heading row.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"content": {
"type": "string",
"default": "Cart totals"
},
"lock": {
"type": "object",
"default": {
"remove": false,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,22 @@
/**
* External dependencies
*/
import classnames from 'classnames';
const Block = ( {
className,
content = '',
}: {
className: string;
content: string;
} ): JSX.Element => {
return (
<span
className={ classnames( className, 'wc-block-cart__totals-title' ) }
>
{ content }
</span>
);
};
export default Block;

View File

@@ -0,0 +1,47 @@
/**
* External dependencies
*/
import { PlainText, useBlockProps } from '@wordpress/block-editor';
import classnames from 'classnames';
/**
* Internal dependencies
*/
import './editor.scss';
export const Edit = ( {
attributes,
setAttributes,
}: {
attributes: {
content: string;
className: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { content = '', className = '' } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<span
className={ classnames(
className,
'wc-block-cart__totals-title'
) }
>
<PlainText
className={ '' }
value={ content }
onChange={ ( value ) =>
setAttributes( { content: value } )
}
style={ { backgroundColor: 'transparent' } }
/>
</span>
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,8 @@
.wp-block-woocommerce-cart-order-summary-heading-block {
textarea {
text-align: right;
text-transform: uppercase;
line-height: 1;
height: auto;
}
}

View File

@@ -0,0 +1,12 @@
/**
* External dependencies
*/
import { withFilteredAttributes } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
*/
import Block from './block';
import attributes from './attributes';
export default withFilteredAttributes( attributes )( Block );

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-heading-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,27 @@
{
"name": "woocommerce/cart-order-summary-shipping-block",
"version": "1.0.0",
"title": "Shipping",
"description": "Shows the cart shipping row.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,34 @@
/**
* External dependencies
*/
import { TotalsShipping } from '@woocommerce/base-components/cart-checkout';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks';
import { TotalsWrapper } from '@woocommerce/blocks-components';
import { getSetting } from '@woocommerce/settings';
const Block = ( { className }: { className: string } ): JSX.Element | null => {
const { cartTotals, cartNeedsShipping } = useStoreCart();
if ( ! cartNeedsShipping ) {
return null;
}
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<TotalsWrapper className={ className }>
<TotalsShipping
showCalculator={ getSetting< boolean >(
'isShippingCalculatorEnabled',
true
) }
showRateSelector={ true }
values={ cartTotals }
currency={ totalsCurrency }
/>
</TotalsWrapper>
);
};
export default Block;

View File

@@ -0,0 +1,66 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ExternalLink } from '@wordpress/components';
import { ADMIN_URL, getSetting } from '@woocommerce/settings';
import Noninteractive from '@woocommerce/base-components/noninteractive';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: {
className: string;
lock: {
move: boolean;
remove: boolean;
};
};
} ): JSX.Element => {
const { className } = attributes;
const shippingEnabled = getSetting( 'shippingEnabled', true );
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<InspectorControls>
{ !! shippingEnabled && (
<PanelBody
title={ __(
'Shipping Calculations',
'woo-gutenberg-products-block'
) }
>
<p className="wc-block-checkout__controls-text">
{ __(
'Options that control shipping can be managed in your store settings.',
'woo-gutenberg-products-block'
) }
</p>
<ExternalLink
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=shipping&section=options` }
>
{ __(
'Manage shipping options',
'woo-gutenberg-products-block'
) }
</ExternalLink>{ ' ' }
</PanelBody>
) }
</InspectorControls>
<Noninteractive>
<Block className={ className } />
</Noninteractive>
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-shipping-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,31 @@
{
"name": "woocommerce/cart-order-summary-subtotal-block",
"version": "1.0.0",
"title": "Subtotal",
"description": "Shows the cart subtotal row.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"lock": false
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"lock": {
"type": "object",
"default": {
"remove": true,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,19 @@
/**
* External dependencies
*/
import { Subtotal, TotalsWrapper } from '@woocommerce/blocks-components';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks';
const Block = ( { className = '' }: { className?: string } ): JSX.Element => {
const { cartTotals } = useStoreCart();
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<TotalsWrapper className={ className }>
<Subtotal currency={ totalsCurrency } values={ cartTotals } />
</TotalsWrapper>
);
};
export default Block;

View File

@@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { useBlockProps } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
}: {
attributes: {
className: string;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { className } = attributes;
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<Block className={ className } />
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,6 @@
/**
* Internal dependencies
*/
import Block from './block';
export default Block;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/cart-order-summary-subtotal-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,18 @@
/**
* External dependencies
*/
import { getSetting } from '@woocommerce/settings';
export default {
showRateAfterTaxName: {
type: 'boolean',
default: getSetting( 'displayCartPricesIncludingTax', false ),
},
lock: {
type: 'object',
default: {
remove: true,
move: false,
},
},
};

View File

@@ -0,0 +1,31 @@
{
"name": "woocommerce/cart-order-summary-taxes-block",
"version": "1.0.0",
"title": "Taxes",
"description": "Shows the cart taxes row.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"lock": false
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"lock": {
"type": "object",
"default": {
"remove": true,
"move": false
}
}
},
"parent": [ "woocommerce/cart-order-summary-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,43 @@
/**
* External dependencies
*/
import { TotalsTaxes, TotalsWrapper } from '@woocommerce/blocks-components';
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
import { useStoreCart } from '@woocommerce/base-context/hooks';
import { getSetting } from '@woocommerce/settings';
const Block = ( {
className,
showRateAfterTaxName,
}: {
className: string;
showRateAfterTaxName: boolean;
} ): JSX.Element | null => {
const { cartTotals } = useStoreCart();
const displayCartPricesIncludingTax = getSetting(
'displayCartPricesIncludingTax',
false
);
if (
displayCartPricesIncludingTax ||
parseInt( cartTotals.total_tax, 10 ) <= 0
) {
return null;
}
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
return (
<TotalsWrapper className={ className }>
<TotalsTaxes
showRateAfterTaxName={ showRateAfterTaxName }
currency={ totalsCurrency }
values={ cartTotals }
/>
</TotalsWrapper>
);
};
export default Block;

View File

@@ -0,0 +1,77 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, ToggleControl } from '@wordpress/components';
import { getSetting } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import Block from './block';
export const Edit = ( {
attributes,
setAttributes,
}: {
attributes: {
className: string;
showRateAfterTaxName: boolean;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element => {
const { className, showRateAfterTaxName } = attributes;
const blockProps = useBlockProps();
const taxesEnabled = getSetting( 'taxesEnabled' ) as boolean;
const displayItemizedTaxes = getSetting(
'displayItemizedTaxes',
false
) as boolean;
const displayCartPricesIncludingTax = getSetting(
'displayCartPricesIncludingTax',
false
) as boolean;
return (
<div { ...blockProps }>
<InspectorControls>
{ taxesEnabled &&
displayItemizedTaxes &&
! displayCartPricesIncludingTax && (
<PanelBody
title={ __(
'Taxes',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Show rate after tax name',
'woo-gutenberg-products-block'
) }
help={ __(
'Show the percentage rate alongside each tax line in the summary.',
'woo-gutenberg-products-block'
) }
checked={ showRateAfterTaxName }
onChange={ () =>
setAttributes( {
showRateAfterTaxName:
! showRateAfterTaxName,
} )
}
/>
</PanelBody>
) }
</InspectorControls>
<Block
className={ className }
showRateAfterTaxName={ showRateAfterTaxName }
/>
</div>
);
};
export const Save = (): JSX.Element => {
return <div { ...useBlockProps.save() } />;
};

View File

@@ -0,0 +1,12 @@
/**
* External dependencies
*/
import { withFilteredAttributes } from '@woocommerce/shared-hocs';
/**
* Internal dependencies
*/
import Block from './block';
import attributes from './attributes';
export default withFilteredAttributes( attributes )( Block );

View File

@@ -0,0 +1,26 @@
/**
* External dependencies
*/
import { totals } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import attributes from './attributes';
registerBlockType( 'woocommerce/cart-order-summary-taxes-block', {
icon: {
src: (
<Icon
icon={ totals }
className="wc-block-editor-components-block-icon"
/>
),
},
attributes,
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,35 @@
{
"name": "woocommerce/cart-totals-block",
"version": "1.0.0",
"title": "Cart Totals",
"description": "Column containing the cart totals.",
"category": "woocommerce",
"supports": {
"align": false,
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"checkbox": {
"type": "boolean",
"default": false
},
"text": {
"type": "string",
"required": false
},
"lock": {
"type": "object",
"default": {
"remove": true
}
}
},
"parent": [ "woocommerce/filled-cart-block" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,51 @@
/**
* External dependencies
*/
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { Sidebar } from '@woocommerce/base-components/sidebar-layout';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import type { TemplateArray } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import {
useForcedLayout,
getAllowedBlocks,
} from '../../../cart-checkout-shared';
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
const blockProps = useBlockProps( { className: 'wc-block-cart__sidebar' } );
const allowedBlocks = getAllowedBlocks( innerBlockAreas.CART_TOTALS );
const defaultTemplate = [
[ 'woocommerce/cart-order-summary-block', {}, [] ],
[ 'woocommerce/cart-express-payment-block', {}, [] ],
[ 'woocommerce/proceed-to-checkout-block', {}, [] ],
[ 'woocommerce/cart-accepted-payment-methods-block', {}, [] ],
] as TemplateArray;
useForcedLayout( {
clientId,
registeredBlocks: allowedBlocks,
defaultTemplate,
} );
return (
<Sidebar { ...blockProps }>
<InnerBlocks
allowedBlocks={ allowedBlocks }
template={ defaultTemplate }
templateLock={ false }
renderAppender={ InnerBlocks.ButtonBlockAppender }
/>
</Sidebar>
);
};
export const Save = (): JSX.Element => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,28 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { Sidebar } from '@woocommerce/base-components/sidebar-layout';
/**
* Internal dependencies
*/
import './style.scss';
const FrontendBlock = ( {
children,
className = '',
}: {
children: JSX.Element | JSX.Element[];
className?: string;
} ): JSX.Element => {
return (
<Sidebar
className={ classnames( 'wc-block-cart__sidebar', className ) }
>
{ children }
</Sidebar>
);
};
export default FrontendBlock;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { Icon, column } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import './style.scss';
registerBlockType( 'woocommerce/cart-totals-block', {
icon: {
src: (
<Icon
icon={ column }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,8 @@
.is-mobile,
.is-small,
.is-medium {
.wc-block-cart__sidebar {
margin-bottom: $gap-large;
order: 0;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Internal dependencies
*/
import FILLED_CART from './filled-cart-block/block.json';
import EMPTY_CART from './empty-cart-block/block.json';
import CART_ITEMS from './cart-items-block/block.json';
import CART_EXPRESS_PAYMENT from './cart-express-payment-block/block.json';
import CART_LINE_ITEMS from './cart-line-items-block/block.json';
import CART_CROSS_SELLS from './cart-cross-sells-block/block.json';
import CART_CROSS_SELLS_PRODUCTS from './cart-cross-sells-products/block.json';
import CART_TOTALS from './cart-totals-block/block.json';
import PROCEED_TO_CHECKOUT from './proceed-to-checkout-block/block.json';
import CART_ACCEPTED_PAYMENT_METHODS from './cart-accepted-payment-methods-block/block.json';
import CART_ORDER_SUMMARY from './cart-order-summary-block/block.json';
import CART_ORDER_SUMMARY_SUBTOTAL from './cart-order-summary-subtotal/block.json';
import CART_ORDER_SUMMARY_FEE from './cart-order-summary-fee/block.json';
import CART_ORDER_SUMMARY_DISCOUNT from './cart-order-summary-discount/block.json';
import CART_ORDER_SUMMARY_SHIPPING from './cart-order-summary-shipping/block.json';
import CART_ORDER_SUMMARY_COUPON_FORM from './cart-order-summary-coupon-form/block.json';
import CART_ORDER_SUMMARY_TAXES from './cart-order-summary-taxes/block.json';
import CART_ORDER_SUMMARY_HEADING from './cart-order-summary-heading/block.json';
export default {
FILLED_CART,
EMPTY_CART,
CART_ITEMS,
CART_EXPRESS_PAYMENT,
CART_LINE_ITEMS,
CART_CROSS_SELLS,
CART_CROSS_SELLS_PRODUCTS,
CART_TOTALS,
PROCEED_TO_CHECKOUT,
CART_ACCEPTED_PAYMENT_METHODS,
CART_ORDER_SUMMARY,
CART_ORDER_SUMMARY_SUBTOTAL,
CART_ORDER_SUMMARY_FEE,
CART_ORDER_SUMMARY_DISCOUNT,
CART_ORDER_SUMMARY_SHIPPING,
CART_ORDER_SUMMARY_COUPON_FORM,
CART_ORDER_SUMMARY_TAXES,
CART_ORDER_SUMMARY_HEADING,
};

View File

@@ -0,0 +1,28 @@
{
"name": "woocommerce/empty-cart-block",
"version": "1.0.0",
"title": "Empty Cart",
"description": "Contains blocks that are displayed when the cart is empty.",
"category": "woocommerce",
"supports": {
"align": [ "wide" ],
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/cart" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,105 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import type { TemplateArray } from '@wordpress/blocks';
import { useEditorContext } from '@woocommerce/base-context';
import { SHOP_URL } from '@woocommerce/block-settings';
/**
* Internal dependencies
*/
import {
useForcedLayout,
getAllowedBlocks,
} from '../../../cart-checkout-shared';
const browseStoreTemplate = SHOP_URL
? [
'core/paragraph',
{
align: 'center',
content: sprintf(
/* translators: %s is the link to the store product directory. */
__(
'<a href="%s">Browse store</a>',
'woo-gutenberg-products-block'
),
SHOP_URL
),
dropCap: false,
},
]
: null;
const defaultTemplate = [
[
'core/heading',
{
textAlign: 'center',
content: __(
'Your cart is currently empty!',
'woo-gutenberg-products-block'
),
level: 2,
className: 'with-empty-cart-icon wc-block-cart__empty-cart__title',
},
],
browseStoreTemplate,
[
'core/separator',
{
className: 'is-style-dots',
},
],
[
'core/heading',
{
textAlign: 'center',
content: __( 'New in store', 'woo-gutenberg-products-block' ),
level: 2,
},
],
[
'woocommerce/product-new',
{
columns: 4,
rows: 1,
},
],
].filter( Boolean ) as unknown as TemplateArray;
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
const blockProps = useBlockProps();
const { currentView } = useEditorContext();
const allowedBlocks = getAllowedBlocks( innerBlockAreas.EMPTY_CART );
useForcedLayout( {
clientId,
registeredBlocks: allowedBlocks,
defaultTemplate,
} );
return (
<div
{ ...blockProps }
hidden={ currentView !== 'woocommerce/empty-cart-block' }
>
<InnerBlocks
template={ defaultTemplate }
templateLock={ false }
renderAppender={ InnerBlocks.ButtonBlockAppender }
/>
</div>
);
};
export const Save = (): JSX.Element => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,37 @@
/**
* External dependencies
*/
import { useStoreCart } from '@woocommerce/base-context/hooks';
import { useEffect } from '@wordpress/element';
import { dispatchEvent } from '@woocommerce/base-utils';
/**
* Internal dependencies
*/
import './style.scss';
const FrontendBlock = ( {
children,
className,
}: {
children: JSX.Element;
className: string;
} ): JSX.Element | null => {
const { cartItems, cartIsLoading } = useStoreCart();
useEffect( () => {
if ( cartItems.length !== 0 || cartIsLoading ) {
return;
}
dispatchEvent( 'wc-blocks_render_blocks_frontend', {
element: document.body.querySelector(
'.wp-block-woocommerce-cart'
),
} );
}, [ cartIsLoading, cartItems ] );
if ( ! cartIsLoading && cartItems.length === 0 ) {
return <div className={ className }>{ children }</div>;
}
return null;
};
export default FrontendBlock;

View File

@@ -0,0 +1,25 @@
/**
* External dependencies
*/
import { removeCart } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
import './style.scss';
registerBlockType( 'woocommerce/empty-cart-block', {
icon: {
src: (
<Icon
icon={ removeCart }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,22 @@
.wc-block-cart__empty-cart__title,
.editor-styles-wrapper .wc-block-cart__empty-cart__title {
font-size: inherit;
}
.wc-block-cart__empty-cart__title.with-empty-cart-icon {
&::before {
content: "";
background-color: currentColor;
display: block;
margin: 0 auto 2em;
mask-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzOCIgaGVpZ2h0PSIzOCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMTkgMEM4LjUwNCAwIDAgOC41MDQgMCAxOXM4LjUwNCAxOSAxOSAxOSAxOS04LjUwNCAxOS0xOVMyOS40OTYgMCAxOSAwWm02LjEyOSAxMi44NzFhMi40NDkgMi40NDkgMCAwIDEgMi40NTIgMi40NTIgMi40NDkgMi40NDkgMCAwIDEtMi40NTIgMi40NTEgMi40NDkgMi40NDkgMCAwIDEtMi40NTItMi40NTEgMi40NDkgMi40NDkgMCAwIDEgMi40NTItMi40NTJaTTExLjY0NSAzMS4yNThjLTIuMDMgMC0zLjY3Ny0xLjYwOS0zLjY3Ny0zLjYgMC0xLjUzMyAyLjE4My00LjYyOCAzLjE4Ny01Ljk2MWEuNjEuNjEgMCAwIDEgLjk4IDBjMS4wMDQgMS4zMzMgMy4xODggNC40MjggMy4xODggNS45NiAwIDEuOTkyLTEuNjQ4IDMuNjAxLTMuNjc4IDMuNjAxWm0xLjIyNi0xMy40ODRhMi40NDkgMi40NDkgMCAwIDEtMi40NTItMi40NTEgMi40NDkgMi40NDkgMCAwIDEgMi40NTItMi40NTIgMi40NDkgMi40NDkgMCAwIDEgMi40NTIgMi40NTIgMi40NDkgMi40NDkgMCAwIDEtMi40NTIgMi40NTFabTEzLjA0IDExLjgxNEE4Ljk4OSA4Ljk4OSAwIDAgMCAxOSAyNi4zNTVjLTEuNjI0IDAtMS42MjQtMi40NTIgMC0yLjQ1MiAzLjQwMiAwIDYuNjEyIDEuNTAyIDguNzg4IDQuMTIyIDEuMDU3IDEuMjU3LS44NTkgMi43OTYtMS44NzggMS41NjNaIi8+PC9zdmc+);
mask-position: center;
mask-repeat: no-repeat;
mask-size: 5em;
width: 5em;
height: 5em;
}
}
.wp-block-woocommerce-empty-cart-block > .aligncenter {
margin-left: auto !important;
margin-right: auto !important;
}

View File

@@ -0,0 +1,28 @@
{
"name": "woocommerce/filled-cart-block",
"version": "1.0.0",
"title": "Filled Cart",
"description": "Contains blocks that are displayed when the cart contains products.",
"category": "woocommerce",
"supports": {
"align": [ "wide" ],
"html": false,
"multiple": false,
"reusable": false,
"inserter": false,
"lock": false
},
"attributes": {
"lock": {
"type": "object",
"default": {
"remove": true,
"move": true
}
}
},
"parent": [ "woocommerce/cart" ],
"textdomain": "woocommerce",
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2
}

View File

@@ -0,0 +1,62 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { SidebarLayout } from '@woocommerce/base-components/sidebar-layout';
import type { TemplateArray } from '@wordpress/blocks';
import { useEditorContext } from '@woocommerce/base-context';
/**
* Internal dependencies
*/
import {
useForcedLayout,
getAllowedBlocks,
} from '../../../cart-checkout-shared';
import './editor.scss';
import { useCartBlockContext } from '../../context';
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
const blockProps = useBlockProps();
const { currentView } = useEditorContext();
const { hasDarkControls } = useCartBlockContext();
const allowedBlocks = getAllowedBlocks( innerBlockAreas.FILLED_CART );
const defaultTemplate = [
[ 'woocommerce/cart-items-block', {}, [] ],
[ 'woocommerce/cart-totals-block', {}, [] ],
] as TemplateArray;
useForcedLayout( {
clientId,
registeredBlocks: allowedBlocks,
defaultTemplate,
} );
return (
<div
{ ...blockProps }
hidden={ currentView !== 'woocommerce/filled-cart-block' }
>
<SidebarLayout
className={ classnames( 'wc-block-cart', {
'has-dark-controls': hasDarkControls,
} ) }
>
<InnerBlocks
allowedBlocks={ allowedBlocks }
template={ defaultTemplate }
templateLock="insert"
/>
</SidebarLayout>
</div>
);
};
export const Save = (): JSX.Element => {
return (
<div { ...useBlockProps.save() }>
<InnerBlocks.Content />
</div>
);
};

View File

@@ -0,0 +1,23 @@
.wp-block-woocommerce-filled-cart-block {
.wc-block-components-sidebar-layout {
display: block;
}
.block-editor-block-list__layout {
display: flex;
flex-flow: row wrap;
align-items: flex-start;
}
.wc-block-components-main,
.wc-block-components-sidebar,
.block-editor-block-list__layout {
> :first-child {
margin-top: 0;
}
}
.wp-block-woocommerce-cart-totals-block,
.wp-block-woocommerce-cart-items-block {
.block-editor-block-list__layout {
display: block;
}
}
}

View File

@@ -0,0 +1,37 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import { SidebarLayout } from '@woocommerce/base-components/sidebar-layout';
import { useStoreCart } from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import { useCartBlockContext } from '../../context';
const FrontendBlock = ( {
children,
className,
}: {
children: JSX.Element | JSX.Element[];
className: string;
} ): JSX.Element | null => {
const { cartItems, cartIsLoading } = useStoreCart();
const { hasDarkControls } = useCartBlockContext();
if ( cartIsLoading || cartItems.length >= 1 ) {
return (
<SidebarLayout
className={ classnames( 'wc-block-cart', className, {
'has-dark-controls': hasDarkControls,
} ) }
>
{ children }
</SidebarLayout>
);
}
return null;
};
export default FrontendBlock;

View File

@@ -0,0 +1,24 @@
/**
* External dependencies
*/
import { filledCart } from '@woocommerce/icons';
import { Icon } from '@wordpress/icons';
import { registerBlockType } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { Edit, Save } from './edit';
registerBlockType( 'woocommerce/filled-cart-block', {
icon: {
src: (
<Icon
icon={ filledCart }
className="wc-block-editor-components-block-icon"
/>
),
},
edit: Edit,
save: Save,
} );

View File

@@ -0,0 +1,21 @@
/**
* Internal dependencies
*/
import './filled-cart-block';
import './cart-items-block';
import './cart-line-items-block';
import './cart-cross-sells-block';
import './cart-cross-sells-products';
import './cart-totals-block';
import './cart-express-payment-block';
import './proceed-to-checkout-block';
import './empty-cart-block';
import './cart-accepted-payment-methods-block';
import './cart-order-summary-block';
import './cart-order-summary-subtotal';
import './cart-order-summary-fee';
import './cart-order-summary-discount';
import './cart-order-summary-shipping';
import './cart-order-summary-coupon-form';
import './cart-order-summary-taxes';
import './cart-order-summary-heading';

Some files were not shown because too many files have changed in this diff Show More