first commit
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
export const blockName = 'woocommerce/cart';
|
||||
export const blockAttributes = {
|
||||
isPreview: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
save: false,
|
||||
},
|
||||
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',
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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 {
|
||||
ValidationContextProvider,
|
||||
StoreNoticesContainer,
|
||||
SnackbarNoticesContainer,
|
||||
} from '@woocommerce/base-context';
|
||||
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 {
|
||||
StoreNoticesProvider,
|
||||
CartProvider,
|
||||
} from '@woocommerce/base-context/providers';
|
||||
import { SlotFillProvider } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* 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,
|
||||
} }
|
||||
>
|
||||
<ValidationContextProvider>
|
||||
{ children }
|
||||
</ValidationContextProvider>
|
||||
</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…', '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 }
|
||||
>
|
||||
<SnackbarNoticesContainer context="wc/cart" />
|
||||
<StoreNoticesProvider>
|
||||
<StoreNoticesContainer context="wc/cart" />
|
||||
<SlotFillProvider>
|
||||
<CartProvider>
|
||||
<Cart attributes={ attributes }>{ children }</Cart>
|
||||
<ScrollOnError scrollToTop={ scrollToTop } />
|
||||
</CartProvider>
|
||||
</SlotFillProvider>
|
||||
</StoreNoticesProvider>
|
||||
</BlockErrorBoundary>
|
||||
);
|
||||
export default withScrollToTop( Block );
|
||||
@@ -0,0 +1,359 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { speak } from '@wordpress/a11y';
|
||||
import QuantitySelector from '@woocommerce/base-components/quantity-selector';
|
||||
import ProductPrice from '@woocommerce/base-components/product-price';
|
||||
import ProductName from '@woocommerce/base-components/product-name';
|
||||
import {
|
||||
useStoreCartItemQuantity,
|
||||
useStoreEvents,
|
||||
useStoreCart,
|
||||
} from '@woocommerce/base-context/hooks';
|
||||
import {
|
||||
ProductBackorderBadge,
|
||||
ProductImage,
|
||||
ProductLowStockBadge,
|
||||
ProductMetadata,
|
||||
ProductSaleBadge,
|
||||
} from '@woocommerce/base-components/cart-checkout';
|
||||
import {
|
||||
getCurrencyFromPriceResponse,
|
||||
Currency,
|
||||
} from '@woocommerce/price-format';
|
||||
import {
|
||||
__experimentalApplyCheckoutFilter,
|
||||
mustContain,
|
||||
} from '@woocommerce/blocks-checkout';
|
||||
import Dinero from 'dinero.js';
|
||||
import { forwardRef, useMemo } from '@wordpress/element';
|
||||
import type { CartItem } from '@woocommerce/type-defs/cart';
|
||||
import { objectHasProp } from '@woocommerce/types';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Convert a Dinero object with precision to store currency minor unit.
|
||||
*
|
||||
* @param {Dinero} priceObject Price object to convert.
|
||||
* @param {Object} currency Currency data.
|
||||
* @return {number} Amount with new minor unit precision.
|
||||
*/
|
||||
const getAmountFromRawPrice = (
|
||||
priceObject: Dinero.Dinero,
|
||||
currency: Currency
|
||||
) => {
|
||||
return priceObject.convertPrecision( currency.minorUnit ).getAmount();
|
||||
};
|
||||
|
||||
const productPriceValidation = ( value ) => mustContain( value, '<price/>' );
|
||||
|
||||
interface CartLineItemRowProps {
|
||||
lineItem: CartItem | Record< string, never >;
|
||||
onRemove?: () => void;
|
||||
tabIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cart line item table row component.
|
||||
*/
|
||||
const CartLineItemRow = forwardRef< HTMLTableRowElement, CartLineItemRowProps >(
|
||||
(
|
||||
{ lineItem, onRemove = () => void null, tabIndex = null },
|
||||
ref
|
||||
): JSX.Element => {
|
||||
const {
|
||||
name: initialName = '',
|
||||
catalog_visibility: catalogVisibility = 'visible',
|
||||
short_description: shortDescription = '',
|
||||
description: fullDescription = '',
|
||||
low_stock_remaining: lowStockRemaining = null,
|
||||
show_backorder_badge: showBackorderBadge = false,
|
||||
quantity_limits: quantityLimits = {
|
||||
minimum: 1,
|
||||
maximum: 99,
|
||||
multiple_of: 1,
|
||||
editable: true,
|
||||
},
|
||||
sold_individually: soldIndividually = false,
|
||||
permalink = '',
|
||||
images = [],
|
||||
variation = [],
|
||||
item_data: itemData = [],
|
||||
prices = {
|
||||
currency_code: 'USD',
|
||||
currency_minor_unit: 2,
|
||||
currency_symbol: '$',
|
||||
currency_prefix: '$',
|
||||
currency_suffix: '',
|
||||
currency_decimal_separator: '.',
|
||||
currency_thousand_separator: ',',
|
||||
price: '0',
|
||||
regular_price: '0',
|
||||
sale_price: '0',
|
||||
price_range: null,
|
||||
raw_prices: {
|
||||
precision: 6,
|
||||
price: '0',
|
||||
regular_price: '0',
|
||||
sale_price: '0',
|
||||
},
|
||||
},
|
||||
totals = {
|
||||
currency_code: 'USD',
|
||||
currency_minor_unit: 2,
|
||||
currency_symbol: '$',
|
||||
currency_prefix: '$',
|
||||
currency_suffix: '',
|
||||
currency_decimal_separator: '.',
|
||||
currency_thousand_separator: ',',
|
||||
line_subtotal: '0',
|
||||
line_subtotal_tax: '0',
|
||||
},
|
||||
extensions,
|
||||
} = lineItem;
|
||||
|
||||
const { quantity, setItemQuantity, removeItem, isPendingDelete } =
|
||||
useStoreCartItemQuantity( lineItem );
|
||||
const { dispatchStoreEvent } = useStoreEvents();
|
||||
|
||||
// Prepare props to pass to the __experimentalApplyCheckoutFilter filter.
|
||||
// We need to pluck out receiveCart.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { receiveCart, ...cart } = useStoreCart();
|
||||
const arg = useMemo(
|
||||
() => ( {
|
||||
context: 'cart',
|
||||
cartItem: lineItem,
|
||||
cart,
|
||||
} ),
|
||||
[ lineItem, cart ]
|
||||
);
|
||||
const priceCurrency = getCurrencyFromPriceResponse( prices );
|
||||
const name = __experimentalApplyCheckoutFilter( {
|
||||
filterName: 'itemName',
|
||||
defaultValue: initialName,
|
||||
extensions,
|
||||
arg,
|
||||
} );
|
||||
|
||||
const regularAmountSingle = Dinero( {
|
||||
amount: parseInt( prices.raw_prices.regular_price, 10 ),
|
||||
precision: prices.raw_prices.precision,
|
||||
} );
|
||||
const purchaseAmountSingle = Dinero( {
|
||||
amount: parseInt( prices.raw_prices.price, 10 ),
|
||||
precision: prices.raw_prices.precision,
|
||||
} );
|
||||
const saleAmountSingle =
|
||||
regularAmountSingle.subtract( purchaseAmountSingle );
|
||||
const saleAmount = saleAmountSingle.multiply( quantity );
|
||||
const totalsCurrency = getCurrencyFromPriceResponse( totals );
|
||||
let lineSubtotal = parseInt( totals.line_subtotal, 10 );
|
||||
if ( getSetting( 'displayCartPricesIncludingTax', false ) ) {
|
||||
lineSubtotal += parseInt( totals.line_subtotal_tax, 10 );
|
||||
}
|
||||
const subtotalPrice = Dinero( {
|
||||
amount: lineSubtotal,
|
||||
precision: totalsCurrency.minorUnit,
|
||||
} );
|
||||
|
||||
const firstImage = images.length ? images[ 0 ] : {};
|
||||
const isProductHiddenFromCatalog =
|
||||
catalogVisibility === 'hidden' || catalogVisibility === 'search';
|
||||
|
||||
const cartItemClassNameFilter = __experimentalApplyCheckoutFilter( {
|
||||
filterName: 'cartItemClass',
|
||||
defaultValue: '',
|
||||
extensions,
|
||||
arg,
|
||||
} );
|
||||
|
||||
// Allow extensions to filter how the price is displayed. Ie: prepending or appending some values.
|
||||
const productPriceFormat = __experimentalApplyCheckoutFilter( {
|
||||
filterName: 'cartItemPrice',
|
||||
defaultValue: '<price/>',
|
||||
extensions,
|
||||
arg,
|
||||
validation: productPriceValidation,
|
||||
} );
|
||||
|
||||
const subtotalPriceFormat = __experimentalApplyCheckoutFilter( {
|
||||
filterName: 'subtotalPriceFormat',
|
||||
defaultValue: '<price/>',
|
||||
extensions,
|
||||
arg,
|
||||
validation: productPriceValidation,
|
||||
} );
|
||||
|
||||
const saleBadgePriceFormat = __experimentalApplyCheckoutFilter( {
|
||||
filterName: 'saleBadgePriceFormat',
|
||||
defaultValue: '<price/>',
|
||||
extensions,
|
||||
arg,
|
||||
validation: productPriceValidation,
|
||||
} );
|
||||
|
||||
return (
|
||||
<tr
|
||||
className={ classnames(
|
||||
'wc-block-cart-items__row',
|
||||
cartItemClassNameFilter,
|
||||
{
|
||||
'is-disabled': isPendingDelete,
|
||||
}
|
||||
) }
|
||||
ref={ ref }
|
||||
tabIndex={ tabIndex }
|
||||
>
|
||||
{ /* If the image has no alt text, this link is unnecessary and can be hidden. */ }
|
||||
<td
|
||||
className="wc-block-cart-item__image"
|
||||
aria-hidden={
|
||||
! objectHasProp( firstImage, 'alt' ) || ! firstImage.alt
|
||||
}
|
||||
>
|
||||
{ /* We don't need to make it focusable, because product name has the same link. */ }
|
||||
{ isProductHiddenFromCatalog ? (
|
||||
<ProductImage
|
||||
image={ firstImage }
|
||||
fallbackAlt={ name }
|
||||
/>
|
||||
) : (
|
||||
<a href={ permalink } tabIndex={ -1 }>
|
||||
<ProductImage
|
||||
image={ firstImage }
|
||||
fallbackAlt={ name }
|
||||
/>
|
||||
</a>
|
||||
) }
|
||||
</td>
|
||||
<td className="wc-block-cart-item__product">
|
||||
<div className="wc-block-cart-item__wrap">
|
||||
<ProductName
|
||||
disabled={
|
||||
isPendingDelete || isProductHiddenFromCatalog
|
||||
}
|
||||
name={ name }
|
||||
permalink={ permalink }
|
||||
/>
|
||||
{ showBackorderBadge ? (
|
||||
<ProductBackorderBadge />
|
||||
) : (
|
||||
!! lowStockRemaining && (
|
||||
<ProductLowStockBadge
|
||||
lowStockRemaining={ lowStockRemaining }
|
||||
/>
|
||||
)
|
||||
) }
|
||||
|
||||
<div className="wc-block-cart-item__prices">
|
||||
<ProductPrice
|
||||
currency={ priceCurrency }
|
||||
regularPrice={ getAmountFromRawPrice(
|
||||
regularAmountSingle,
|
||||
priceCurrency
|
||||
) }
|
||||
price={ getAmountFromRawPrice(
|
||||
purchaseAmountSingle,
|
||||
priceCurrency
|
||||
) }
|
||||
format={ subtotalPriceFormat }
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ProductSaleBadge
|
||||
currency={ priceCurrency }
|
||||
saleAmount={ getAmountFromRawPrice(
|
||||
saleAmountSingle,
|
||||
priceCurrency
|
||||
) }
|
||||
format={ saleBadgePriceFormat }
|
||||
/>
|
||||
|
||||
<ProductMetadata
|
||||
shortDescription={ shortDescription }
|
||||
fullDescription={ fullDescription }
|
||||
itemData={ itemData }
|
||||
variation={ variation }
|
||||
/>
|
||||
|
||||
<div className="wc-block-cart-item__quantity">
|
||||
{ ! soldIndividually &&
|
||||
!! quantityLimits.editable && (
|
||||
<QuantitySelector
|
||||
disabled={ isPendingDelete }
|
||||
quantity={ quantity }
|
||||
minimum={ quantityLimits.minimum }
|
||||
maximum={ quantityLimits.maximum }
|
||||
step={ quantityLimits.multiple_of }
|
||||
onChange={ ( newQuantity ) => {
|
||||
setItemQuantity( newQuantity );
|
||||
dispatchStoreEvent(
|
||||
'cart-set-item-quantity',
|
||||
{
|
||||
product: lineItem,
|
||||
quantity: newQuantity,
|
||||
}
|
||||
);
|
||||
} }
|
||||
itemName={ name }
|
||||
/>
|
||||
) }
|
||||
<button
|
||||
className="wc-block-cart-item__remove-link"
|
||||
onClick={ () => {
|
||||
onRemove();
|
||||
removeItem();
|
||||
dispatchStoreEvent( 'cart-remove-item', {
|
||||
product: lineItem,
|
||||
quantity,
|
||||
} );
|
||||
speak(
|
||||
sprintf(
|
||||
/* translators: %s refers to the item name in the cart. */
|
||||
__(
|
||||
'%s has been removed from your cart.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
name
|
||||
)
|
||||
);
|
||||
} }
|
||||
disabled={ isPendingDelete }
|
||||
>
|
||||
{ __(
|
||||
'Remove item',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="wc-block-cart-item__total">
|
||||
<div className="wc-block-cart-item__total-price-and-sale-badge-wrapper">
|
||||
<ProductPrice
|
||||
currency={ totalsCurrency }
|
||||
format={ productPriceFormat }
|
||||
price={ subtotalPrice.getAmount() }
|
||||
/>
|
||||
|
||||
{ quantity > 1 && (
|
||||
<ProductSaleBadge
|
||||
currency={ priceCurrency }
|
||||
saleAmount={ getAmountFromRawPrice(
|
||||
saleAmount,
|
||||
priceCurrency
|
||||
) }
|
||||
format={ saleBadgePriceFormat }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default CartLineItemRow;
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { CartResponseItem } from '@woocommerce/type-defs/cart-response';
|
||||
import { createRef, useEffect, useRef } from '@wordpress/element';
|
||||
import type { RefObject } from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CartLineItemRow from './cart-line-item-row';
|
||||
|
||||
const placeholderRows = [ ...Array( 3 ) ].map( ( _x, i ) => (
|
||||
<CartLineItemRow lineItem={ {} } key={ i } />
|
||||
) );
|
||||
|
||||
interface CartLineItemsTableProps {
|
||||
lineItems: CartResponseItem[];
|
||||
isLoading: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const setRefs = ( lineItems: CartResponseItem[] ) => {
|
||||
const refs = {} as Record< string, RefObject< HTMLTableRowElement > >;
|
||||
lineItems.forEach( ( { key } ) => {
|
||||
refs[ key ] = createRef();
|
||||
} );
|
||||
return refs;
|
||||
};
|
||||
|
||||
const CartLineItemsTable = ( {
|
||||
lineItems = [],
|
||||
isLoading = false,
|
||||
className,
|
||||
}: CartLineItemsTableProps ): JSX.Element => {
|
||||
const tableRef = useRef< HTMLTableElement | null >( null );
|
||||
const rowRefs = useRef( setRefs( lineItems ) );
|
||||
useEffect( () => {
|
||||
rowRefs.current = setRefs( lineItems );
|
||||
}, [ lineItems ] );
|
||||
|
||||
const onRemoveRow = ( nextItemKey: string | null ) => () => {
|
||||
if (
|
||||
rowRefs?.current &&
|
||||
nextItemKey &&
|
||||
rowRefs.current[ nextItemKey ].current instanceof HTMLElement
|
||||
) {
|
||||
( rowRefs.current[ nextItemKey ].current as HTMLElement ).focus();
|
||||
} else if ( tableRef.current instanceof HTMLElement ) {
|
||||
tableRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const products = isLoading
|
||||
? placeholderRows
|
||||
: lineItems.map( ( lineItem, i ) => {
|
||||
const nextItemKey =
|
||||
lineItems.length > i + 1 ? lineItems[ i + 1 ].key : null;
|
||||
return (
|
||||
<CartLineItemRow
|
||||
key={ lineItem.key }
|
||||
lineItem={ lineItem }
|
||||
onRemove={ onRemoveRow( nextItemKey ) }
|
||||
ref={ rowRefs.current[ lineItem.key ] }
|
||||
tabIndex={ -1 }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
return (
|
||||
<table
|
||||
className={ classnames( 'wc-block-cart-items', className ) }
|
||||
ref={ tableRef }
|
||||
tabIndex={ -1 }
|
||||
>
|
||||
<thead>
|
||||
<tr className="wc-block-cart-items__header">
|
||||
<th className="wc-block-cart-items__header-image">
|
||||
<span>
|
||||
{ __( 'Product', 'woo-gutenberg-products-block' ) }
|
||||
</span>
|
||||
</th>
|
||||
<th className="wc-block-cart-items__header-product">
|
||||
<span>
|
||||
{ __( 'Details', 'woo-gutenberg-products-block' ) }
|
||||
</span>
|
||||
</th>
|
||||
<th className="wc-block-cart-items__header-total">
|
||||
<span>
|
||||
{ __( 'Total', 'woo-gutenberg-products-block' ) }
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>{ products }</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
export default CartLineItemsTable;
|
||||
@@ -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 );
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
/* tslint:disable */
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { CartCheckoutFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt';
|
||||
import {
|
||||
useBlockProps,
|
||||
InnerBlocks,
|
||||
InspectorControls,
|
||||
BlockControls,
|
||||
} from '@wordpress/block-editor';
|
||||
import { PanelBody, ToggleControl, Notice } from '@wordpress/components';
|
||||
import { CartCheckoutCompatibilityNotice } from '@woocommerce/editor-components/compatibility-notices';
|
||||
import { CART_PAGE_ID } from '@woocommerce/block-settings';
|
||||
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
|
||||
import {
|
||||
EditorProvider,
|
||||
useEditorContext,
|
||||
CartProvider,
|
||||
} from '@woocommerce/base-context';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
import { getAdminLink } from '@woocommerce/settings';
|
||||
import { previewCart } from '@woocommerce/resource-previews';
|
||||
import { filledCart, removeCart } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './inner-blocks';
|
||||
import './editor.scss';
|
||||
import {
|
||||
addClassToBody,
|
||||
useViewSwitcher,
|
||||
useBlockPropsWithLocking,
|
||||
useForcedLayout,
|
||||
} from '../cart-checkout-shared';
|
||||
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',
|
||||
];
|
||||
|
||||
const views = [
|
||||
{
|
||||
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 } />,
|
||||
},
|
||||
];
|
||||
|
||||
const BlockSettings = ( { attributes, setAttributes } ) => {
|
||||
const { hasDarkControls } = attributes;
|
||||
const { currentPostId } = useEditorContext();
|
||||
return (
|
||||
<InspectorControls>
|
||||
{ currentPostId !== CART_PAGE_ID && (
|
||||
<Notice
|
||||
className="wc-block-cart__page-notice"
|
||||
isDismissible={ false }
|
||||
status="warning"
|
||||
>
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
'If you would like to use this block as your default cart you must update your <a>page settings in WooCommerce</a>.',
|
||||
'woocommerce'
|
||||
),
|
||||
{
|
||||
a: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content
|
||||
<a
|
||||
href={ getAdminLink(
|
||||
'admin.php?page=wc-settings&tab=advanced'
|
||||
) }
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
</Notice>
|
||||
) }
|
||||
<PanelBody title={ __( 'Style', 'woocommerce' ) }>
|
||||
<ToggleControl
|
||||
label={ __(
|
||||
'Dark mode inputs',
|
||||
'woocommerce'
|
||||
) }
|
||||
help={ __(
|
||||
'Inputs styled specifically for use on dark background colors.',
|
||||
'woocommerce'
|
||||
) }
|
||||
checked={ hasDarkControls }
|
||||
onChange={ () =>
|
||||
setAttributes( {
|
||||
hasDarkControls: ! hasDarkControls,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
<CartCheckoutFeedbackPrompt />
|
||||
</InspectorControls>
|
||||
);
|
||||
};
|
||||
|
||||
export const Edit = ( { className, attributes, setAttributes, clientId } ) => {
|
||||
const { hasDarkControls } = attributes;
|
||||
const { currentView, component: ViewSwitcherComponent } = useViewSwitcher(
|
||||
clientId,
|
||||
views
|
||||
);
|
||||
const defaultTemplate = [
|
||||
[ 'woocommerce/filled-cart-block', {}, [] ],
|
||||
[ 'woocommerce/empty-cart-block', {}, [] ],
|
||||
];
|
||||
const blockProps = useBlockPropsWithLocking( {
|
||||
className: classnames( className, 'wp-block-woocommerce-cart', {
|
||||
'is-editor-preview': attributes.isPreview,
|
||||
} ),
|
||||
} );
|
||||
useForcedLayout( {
|
||||
clientId,
|
||||
registeredBlocks: ALLOWED_BLOCKS,
|
||||
defaultTemplate,
|
||||
} );
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<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
|
||||
currentView={ currentView }
|
||||
previewData={ { previewCart } }
|
||||
>
|
||||
<BlockSettings
|
||||
attributes={ attributes }
|
||||
setAttributes={ setAttributes }
|
||||
/>
|
||||
<BlockControls __experimentalShareWithChildBlocks>
|
||||
{ ViewSwitcherComponent }
|
||||
</BlockControls>
|
||||
<CartBlockContext.Provider
|
||||
value={ {
|
||||
hasDarkControls,
|
||||
} }
|
||||
>
|
||||
<CartProvider>
|
||||
<InnerBlocks
|
||||
allowedBlocks={ ALLOWED_BLOCKS }
|
||||
template={ defaultTemplate }
|
||||
templateLock={ false }
|
||||
/>
|
||||
</CartProvider>
|
||||
</CartBlockContext.Provider>
|
||||
</EditorProvider>
|
||||
</BlockErrorBoundary>
|
||||
<CartCheckoutCompatibilityNotice blockName="cart" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = () => {
|
||||
return (
|
||||
<div
|
||||
{ ...useBlockProps.save( {
|
||||
className: 'is-loading',
|
||||
} ) }
|
||||
>
|
||||
<InnerBlocks.Content />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
.wc-block-cart__page-notice {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
} );
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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 { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
import { 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,
|
||||
__experimentalExposeControlsToChildren: true,
|
||||
},
|
||||
example: {
|
||||
attributes: {
|
||||
isPreview: true,
|
||||
},
|
||||
},
|
||||
attributes: blockAttributes,
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
// 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'
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
registerFeaturePluginBlockType( blockName, settings );
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
import { Icon, payment } from '@wordpress/icons';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ payment }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "woocommerce/cart-express-payment-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Express Checkout",
|
||||
"description": "Provide an express payment option for your customers.",
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -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;
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import { Placeholder, Button } from 'wordpress-components';
|
||||
import { useExpressPaymentMethods } from '@woocommerce/base-context/hooks';
|
||||
import { Icon, payment } from '@wordpress/icons';
|
||||
import { ADMIN_URL } from '@woocommerce/settings';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
import './editor.scss';
|
||||
|
||||
/**
|
||||
* Renders a placeholder in the editor.
|
||||
*/
|
||||
const NoExpressPaymentMethodsPlaceholder = () => {
|
||||
return (
|
||||
<Placeholder
|
||||
icon={ <Icon icon={ payment } /> }
|
||||
label={ __( 'Express Checkout', 'woo-gutenberg-products-block' ) }
|
||||
className="wp-block-woocommerce-checkout-express-payment-block-placeholder"
|
||||
>
|
||||
<span className="wp-block-woocommerce-checkout-express-payment-block-placeholder__description">
|
||||
{ __(
|
||||
"Your store doesn't have any Payment Methods that support the Express Checkout Block. If they are added, they will be shown here.",
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</span>
|
||||
<Button
|
||||
isPrimary
|
||||
href={ `${ ADMIN_URL }admin.php?page=wc-settings&tab=checkout` }
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="wp-block-woocommerce-checkout-express-payment-block-placeholder__button"
|
||||
>
|
||||
{ __(
|
||||
'Configure Payment Methods',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
</Button>
|
||||
</Placeholder>
|
||||
);
|
||||
};
|
||||
|
||||
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 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
{ hasExpressPaymentMethods ? (
|
||||
<Block className={ className } />
|
||||
) : (
|
||||
<NoExpressPaymentMethodsPlaceholder />
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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-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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, payment } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ payment }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "woocommerce/cart-items-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Cart Items block",
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
export const Edit = ( { clientId }: { clientId: string } ): JSX.Element => {
|
||||
const blockProps = useBlockProps( { className: 'wc-block-cart__main' } );
|
||||
const allowedBlocks = getAllowedBlocks( innerBlockAreas.CART_ITEMS );
|
||||
const defaultTemplate = [
|
||||
[ 'woocommerce/cart-line-items-block', {}, [] ],
|
||||
] 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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, column } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ column }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useStoreCart } from '@woocommerce/base-context/hooks';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CartLineItemsTable from '../../cart-line-items-table';
|
||||
|
||||
const Block = ( { className }: { className: string } ): JSX.Element => {
|
||||
const { cartItems, cartIsLoading } = useStoreCart();
|
||||
return (
|
||||
<CartLineItemsTable
|
||||
className={ className }
|
||||
lineItems={ cartItems }
|
||||
isLoading={ cartIsLoading }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, column } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ column }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -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-subtotal-block', {}, [] ],
|
||||
[ 'woocommerce/cart-order-summary-fee-block', {}, [] ],
|
||||
[ 'woocommerce/cart-order-summary-discount-block', {}, [] ],
|
||||
[ 'woocommerce/cart-order-summary-coupon-form-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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -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 } />;
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -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-checkout';
|
||||
|
||||
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;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, tag } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ tag }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TotalsDiscount } from '@woocommerce/base-components/cart-checkout';
|
||||
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
|
||||
import {
|
||||
useStoreCartCoupons,
|
||||
useStoreCart,
|
||||
} from '@woocommerce/base-context/hooks';
|
||||
import {
|
||||
ExperimentalDiscountsMeta,
|
||||
TotalsWrapper,
|
||||
} 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;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TotalsFees, TotalsWrapper } from '@woocommerce/blocks-checkout';
|
||||
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;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import Title from '@woocommerce/base-components/title';
|
||||
import classnames from 'classnames';
|
||||
|
||||
const Block = ( {
|
||||
className,
|
||||
content = '',
|
||||
}: {
|
||||
className: string;
|
||||
content: string;
|
||||
} ): JSX.Element => {
|
||||
return (
|
||||
<Title
|
||||
headingLevel="2"
|
||||
className={ classnames( className, 'wc-block-cart__totals-title' ) }
|
||||
>
|
||||
{ content }
|
||||
</Title>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { PlainText, useBlockProps } from '@wordpress/block-editor';
|
||||
import Title from '@woocommerce/base-components/title';
|
||||
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 }>
|
||||
<Title
|
||||
headingLevel="2"
|
||||
className={ classnames(
|
||||
className,
|
||||
'wc-block-cart__totals-title'
|
||||
) }
|
||||
>
|
||||
<PlainText
|
||||
className={ '' }
|
||||
value={ content }
|
||||
onChange={ ( value ) =>
|
||||
setAttributes( { content: value } )
|
||||
}
|
||||
/>
|
||||
</Title>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
.wp-block-woocommerce-cart-order-summary-heading-block {
|
||||
textarea {
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
export default {
|
||||
isShippingCalculatorEnabled: {
|
||||
type: 'boolean',
|
||||
default: getSetting( 'isShippingCalculatorEnabled', true ),
|
||||
},
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: false,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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-checkout';
|
||||
|
||||
const Block = ( {
|
||||
className,
|
||||
isShippingCalculatorEnabled,
|
||||
}: {
|
||||
className: string;
|
||||
isShippingCalculatorEnabled: boolean;
|
||||
} ): JSX.Element | null => {
|
||||
const { cartTotals, cartNeedsShipping } = useStoreCart();
|
||||
|
||||
if ( ! cartNeedsShipping ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
|
||||
|
||||
return (
|
||||
<TotalsWrapper className={ className }>
|
||||
<TotalsShipping
|
||||
showCalculator={ isShippingCalculatorEnabled }
|
||||
showRateSelector={ true }
|
||||
values={ cartTotals }
|
||||
currency={ totalsCurrency }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
|
||||
import { PanelBody, ToggleControl } from '@wordpress/components';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import Noninteractive from '@woocommerce/base-components/noninteractive';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
isShippingCalculatorEnabled: boolean;
|
||||
className: string;
|
||||
lock: {
|
||||
move: boolean;
|
||||
remove: boolean;
|
||||
};
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const { isShippingCalculatorEnabled, className } = attributes;
|
||||
const shippingEnabled = getSetting( 'shippingEnabled', true );
|
||||
const blockProps = useBlockProps();
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InspectorControls>
|
||||
{ !! shippingEnabled && (
|
||||
<PanelBody
|
||||
title={ __(
|
||||
'Shipping rates',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
>
|
||||
<ToggleControl
|
||||
label={ __(
|
||||
'Shipping calculator',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
help={ __(
|
||||
'Allow customers to estimate shipping by entering their address.',
|
||||
'woo-gutenberg-products-block'
|
||||
) }
|
||||
checked={ isShippingCalculatorEnabled }
|
||||
onChange={ () =>
|
||||
setAttributes( {
|
||||
isShippingCalculatorEnabled:
|
||||
! isShippingCalculatorEnabled,
|
||||
} )
|
||||
}
|
||||
/>
|
||||
</PanelBody>
|
||||
) }
|
||||
</InspectorControls>
|
||||
<Noninteractive>
|
||||
<Block
|
||||
className={ className }
|
||||
isShippingCalculatorEnabled={ isShippingCalculatorEnabled }
|
||||
/>
|
||||
</Noninteractive>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
||||
@@ -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 );
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import attributes from './attributes';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
attributes,
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Subtotal, TotalsWrapper } from '@woocommerce/blocks-checkout';
|
||||
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;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TotalsTaxes, TotalsWrapper } from '@woocommerce/blocks-checkout';
|
||||
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;
|
||||
@@ -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() } />;
|
||||
};
|
||||
@@ -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 );
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { totals } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import attributes from './attributes';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ totals }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
attributes,
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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 './style.scss';
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, column } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ column }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,8 @@
|
||||
.is-mobile,
|
||||
.is-small,
|
||||
.is-medium {
|
||||
.wc-block-cart__sidebar {
|
||||
margin-bottom: $gap-large;
|
||||
order: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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_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_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,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
* 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';
|
||||
import iconDataUri from './icon-data-uri.js';
|
||||
import './style.scss';
|
||||
|
||||
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/image',
|
||||
{
|
||||
align: 'center',
|
||||
url: iconDataUri,
|
||||
sizeSlug: 'small',
|
||||
},
|
||||
],
|
||||
[
|
||||
'core/heading',
|
||||
{
|
||||
textAlign: 'center',
|
||||
content: __(
|
||||
'Your cart is currently empty!',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
level: 2,
|
||||
className: '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: 3,
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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( () => {
|
||||
dispatchEvent( 'wc-blocks_render_blocks_frontend', {
|
||||
element: document.body.querySelector(
|
||||
'.wp-block-woocommerce-cart'
|
||||
),
|
||||
} );
|
||||
}, [] );
|
||||
if ( ! cartIsLoading && cartItems.length === 0 ) {
|
||||
return <div className={ className }>{ children }</div>;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default FrontendBlock;
|
||||
@@ -0,0 +1 @@
|
||||
export default 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzgiIGhlaWdodD0iMzgiIHZpZXdCb3g9IjAgMCAzOCAzOCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5IDBDOC41MDQwMyAwIDAgOC41MDQwMyAwIDE5QzAgMjkuNDk2IDguNTA0MDMgMzggMTkgMzhDMjkuNDk2IDM4IDM4IDI5LjQ5NiAzOCAxOUMzOCA4LjUwNDAzIDI5LjQ5NiAwIDE5IDBaTTI1LjEyOSAxMi44NzFDMjYuNDg1MSAxMi44NzEgMjcuNTgwNiAxMy45NjY1IDI3LjU4MDYgMTUuMzIyNkMyNy41ODA2IDE2LjY3ODYgMjYuNDg1MSAxNy43NzQyIDI1LjEyOSAxNy43NzQyQzIzLjc3MyAxNy43NzQyIDIyLjY3NzQgMTYuNjc4NiAyMi42Nzc0IDE1LjMyMjZDMjIuNjc3NCAxMy45NjY1IDIzLjc3MyAxMi44NzEgMjUuMTI5IDEyLjg3MVpNMTEuNjQ1MiAzMS4yNTgxQzkuNjE0OTIgMzEuMjU4MSA3Ljk2Nzc0IDI5LjY0OTIgNy45Njc3NCAyNy42NTczQzcuOTY3NzQgMjYuMTI1IDEwLjE1MTIgMjMuMDI5OCAxMS4xNTQ4IDIxLjY5NjhDMTEuNCAyMS4zNjczIDExLjg5MDMgMjEuMzY3MyAxMi4xMzU1IDIxLjY5NjhDMTMuMTM5MSAyMy4wMjk4IDE1LjMyMjYgMjYuMTI1IDE1LjMyMjYgMjcuNjU3M0MxNS4zMjI2IDI5LjY0OTIgMTMuNjc1NCAzMS4yNTgxIDExLjY0NTIgMzEuMjU4MVpNMTIuODcxIDE3Ljc3NDJDMTEuNTE0OSAxNy43NzQyIDEwLjQxOTQgMTYuNjc4NiAxMC40MTk0IDE1LjMyMjZDMTAuNDE5NCAxMy45NjY1IDExLjUxNDkgMTIuODcxIDEyLjg3MSAxMi44NzFDMTQuMjI3IDEyLjg3MSAxNS4zMjI2IDEzLjk2NjUgMTUuMzIyNiAxNS4zMjI2QzE1LjMyMjYgMTYuNjc4NiAxNC4yMjcgMTcuNzc0MiAxMi44NzEgMTcuNzc0MlpNMjUuOTEwNSAyOS41ODc5QzI0LjE5NDQgMjcuNTM0NyAyMS42NzM4IDI2LjM1NDggMTkgMjYuMzU0OEMxNy4zNzU4IDI2LjM1NDggMTcuMzc1OCAyMy45MDMyIDE5IDIzLjkwMzJDMjIuNDAxNiAyMy45MDMyIDI1LjYxMTcgMjUuNDA0OCAyNy43ODc1IDI4LjAyNUMyOC44NDQ4IDI5LjI4MTUgMjYuOTI5NCAzMC44MjE0IDI1LjkxMDUgMjkuNTg3OVoiIGZpbGw9ImJsYWNrIi8+Cjwvc3ZnPgo=';
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { removeCart } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ removeCart }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,4 @@
|
||||
.wc-block-cart__empty-cart__title,
|
||||
.editor-styles-wrapper .wc-block-cart__empty-cart__title {
|
||||
font-size: inherit;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"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": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { SidebarLayout } from '@woocommerce/base-components/sidebar-layout';
|
||||
import { useStoreCart } from '@woocommerce/base-context/hooks';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { useCartBlockContext } from '../../context';
|
||||
|
||||
const FrontendBlock = ( {
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
className: string;
|
||||
} ): JSX.Element | null => {
|
||||
const { cartItems, cartIsLoading, cartItemErrors } = useStoreCart();
|
||||
const { hasDarkControls } = useCartBlockContext();
|
||||
const { createErrorNotice } = useDispatch( 'core/notices' );
|
||||
|
||||
// Ensures any cart errors listed in the API response get shown.
|
||||
useEffect( () => {
|
||||
cartItemErrors.forEach( ( error ) => {
|
||||
createErrorNotice( decodeEntities( error.message ), {
|
||||
isDismissible: true,
|
||||
id: error.code,
|
||||
context: 'wc/cart',
|
||||
} );
|
||||
} );
|
||||
}, [ createErrorNotice, cartItemErrors ] );
|
||||
|
||||
if ( cartIsLoading || cartItems.length >= 1 ) {
|
||||
return (
|
||||
<SidebarLayout
|
||||
className={ classnames( 'wc-block-cart', className, {
|
||||
'has-dark-controls': hasDarkControls,
|
||||
} ) }
|
||||
>
|
||||
{ children }
|
||||
</SidebarLayout>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default FrontendBlock;
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { filledCart } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ filledCart }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './filled-cart-block';
|
||||
import './cart-items-block';
|
||||
import './cart-line-items-block';
|
||||
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';
|
||||
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
checkoutPageId: {
|
||||
type: 'number',
|
||||
default: 0,
|
||||
},
|
||||
lock: {
|
||||
type: 'object',
|
||||
default: {
|
||||
move: true,
|
||||
remove: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "woocommerce/proceed-to-checkout-block",
|
||||
"version": "1.0.0",
|
||||
"title": "Proceed to checkout",
|
||||
"description": "Allow customers proceed to Checkout.",
|
||||
"category": "woocommerce",
|
||||
"supports": {
|
||||
"align": false,
|
||||
"html": false,
|
||||
"multiple": false,
|
||||
"reusable": false,
|
||||
"inserter": false,
|
||||
"lock": false
|
||||
},
|
||||
"attributes": {
|
||||
"lock": {
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": [ "woocommerce/cart-totals-block" ],
|
||||
"textdomain": "woo-gutenberg-products-block",
|
||||
"apiVersion": 2
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classnames from 'classnames';
|
||||
import { useState, useEffect } from '@wordpress/element';
|
||||
import Button from '@woocommerce/base-components/button';
|
||||
import { CHECKOUT_URL } from '@woocommerce/block-settings';
|
||||
import { useCheckoutContext } from '@woocommerce/base-context';
|
||||
import { usePositionRelativeToViewport } from '@woocommerce/base-hooks';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import './style.scss';
|
||||
|
||||
/**
|
||||
* Checkout button rendered in the full cart page.
|
||||
*/
|
||||
const Block = ( {
|
||||
checkoutPageId,
|
||||
className,
|
||||
}: {
|
||||
checkoutPageId: number;
|
||||
className: string;
|
||||
} ): JSX.Element => {
|
||||
const link = getSetting( 'page-' + checkoutPageId, false );
|
||||
const { isCalculating } = useCheckoutContext();
|
||||
const [ positionReferenceElement, positionRelativeToViewport ] =
|
||||
usePositionRelativeToViewport();
|
||||
const [ showSpinner, setShowSpinner ] = useState( false );
|
||||
|
||||
useEffect( () => {
|
||||
// Add a listener to remove the spinner on the checkout button, so the saved page snapshot does not
|
||||
// contain the spinner class. See https://archive.is/lOEW0 for why this is needed for Safari.
|
||||
|
||||
if (
|
||||
typeof global.addEventListener !== 'function' ||
|
||||
typeof global.removeEventListener !== 'function'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hideSpinner = () => {
|
||||
setShowSpinner( false );
|
||||
};
|
||||
|
||||
global.addEventListener( 'pageshow', hideSpinner );
|
||||
|
||||
return () => {
|
||||
global.removeEventListener( 'pageshow', hideSpinner );
|
||||
};
|
||||
}, [] );
|
||||
|
||||
const submitContainerContents = (
|
||||
<Button
|
||||
className="wc-block-cart__submit-button"
|
||||
href={ link || CHECKOUT_URL }
|
||||
disabled={ isCalculating }
|
||||
onClick={ () => setShowSpinner( true ) }
|
||||
showSpinner={ showSpinner }
|
||||
>
|
||||
{ __( 'Proceed to Checkout', 'woo-gutenberg-products-block' ) }
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={ classnames( 'wc-block-cart__submit', className ) }>
|
||||
{ positionReferenceElement }
|
||||
{ /* The non-sticky container must always be visible because it gives height to its parent, which is required to calculate when it becomes visible in the viewport. */ }
|
||||
<div className="wc-block-cart__submit-container">
|
||||
{ submitContainerContents }
|
||||
</div>
|
||||
{ /* If the positionReferenceElement is below the viewport, display the sticky container. */ }
|
||||
{ positionRelativeToViewport === 'below' && (
|
||||
<div className="wc-block-cart__submit-container wc-block-cart__submit-container--sticky">
|
||||
{ submitContainerContents }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useRef } from '@wordpress/element';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
|
||||
import PageSelector from '@woocommerce/editor-components/page-selector';
|
||||
import { CART_PAGE_ID } from '@woocommerce/block-settings';
|
||||
import Noninteractive from '@woocommerce/base-components/noninteractive';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import Block from './block';
|
||||
export const Edit = ( {
|
||||
attributes,
|
||||
setAttributes,
|
||||
}: {
|
||||
attributes: {
|
||||
checkoutPageId: number;
|
||||
className: string;
|
||||
};
|
||||
setAttributes: ( attributes: Record< string, unknown > ) => void;
|
||||
} ): JSX.Element => {
|
||||
const blockProps = useBlockProps();
|
||||
const { checkoutPageId = 0, className } = attributes;
|
||||
const { current: savedCheckoutPageId } = useRef( checkoutPageId );
|
||||
const currentPostId = useSelect(
|
||||
( select ) => {
|
||||
if ( ! savedCheckoutPageId ) {
|
||||
const store = select( 'core/editor' );
|
||||
return store.getCurrentPostId();
|
||||
}
|
||||
return savedCheckoutPageId;
|
||||
},
|
||||
[ savedCheckoutPageId ]
|
||||
);
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<InspectorControls>
|
||||
{ ! (
|
||||
currentPostId === CART_PAGE_ID && savedCheckoutPageId === 0
|
||||
) && (
|
||||
<PageSelector
|
||||
pageId={ checkoutPageId }
|
||||
setPageId={ ( id ) =>
|
||||
setAttributes( { checkoutPageId: id } )
|
||||
}
|
||||
labels={ {
|
||||
title: __(
|
||||
'Proceed to Checkout button',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
default: __(
|
||||
'WooCommerce Checkout Page',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</InspectorControls>
|
||||
<Noninteractive>
|
||||
<Block
|
||||
checkoutPageId={ checkoutPageId }
|
||||
className={ className }
|
||||
/>
|
||||
</Noninteractive>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Save = (): JSX.Element => {
|
||||
return <div { ...useBlockProps.save() } />;
|
||||
};
|
||||
@@ -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 );
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Icon, button } from '@wordpress/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import attributes from './attributes';
|
||||
import { Edit, Save } from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
registerFeaturePluginBlockType( metadata, {
|
||||
icon: {
|
||||
src: (
|
||||
<Icon
|
||||
icon={ button }
|
||||
className="wc-block-editor-components-block-icon"
|
||||
/>
|
||||
),
|
||||
},
|
||||
attributes,
|
||||
edit: Edit,
|
||||
save: Save,
|
||||
} );
|
||||
@@ -0,0 +1,46 @@
|
||||
.wc-block-cart__submit {
|
||||
position: relative;
|
||||
margin: 0 0 $gap 0;
|
||||
}
|
||||
|
||||
.wc-block-cart__submit-button {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.wc-block-cart {
|
||||
.wc-block-cart__submit-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint(">782px") {
|
||||
.wc-block-cart .wc-block-cart__submit-container--sticky {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint("<782px") {
|
||||
.wc-block-cart .wc-block-cart__submit-container--sticky {
|
||||
background: $white;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: $gap;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
|
||||
&::before {
|
||||
box-shadow: 0 -10px 20px 10px currentColor;
|
||||
color: color.adjust($gray-400, $alpha: -0.5);
|
||||
content: "";
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { lazy } from '@wordpress/element';
|
||||
import { WC_BLOCKS_BUILD_URL } from '@woocommerce/block-settings';
|
||||
import { registerCheckoutBlock } from '@woocommerce/blocks-checkout';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import metadata from './component-metadata';
|
||||
|
||||
// Modify webpack publicPath at runtime based on location of WordPress Plugin.
|
||||
// eslint-disable-next-line no-undef,camelcase
|
||||
__webpack_public_path__ = WC_BLOCKS_BUILD_URL;
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.FILLED_CART,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/filled-cart" */
|
||||
'./filled-cart-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.EMPTY_CART,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/empty-cart" */
|
||||
'./empty-cart-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ITEMS,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/cart-items" */
|
||||
'./cart-items-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_LINE_ITEMS,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/cart-line-items" */
|
||||
'./cart-line-items-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_TOTALS,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/cart-totals" */
|
||||
'./cart-totals-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_EXPRESS_PAYMENT,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/cart-express-payment" */
|
||||
'./cart-express-payment-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.PROCEED_TO_CHECKOUT,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/proceed-to-checkout" */
|
||||
'./proceed-to-checkout-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ACCEPTED_PAYMENT_METHODS,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/cart-accepted-payment-methods" */
|
||||
'./cart-accepted-payment-methods-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/cart-order-summary" */
|
||||
'./cart-order-summary-block/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_HEADING,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-heading" */
|
||||
'./cart-order-summary-heading/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_SUBTOTAL,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-subtotal" */
|
||||
'./cart-order-summary-subtotal/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_FEE,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-fee" */
|
||||
'./cart-order-summary-fee/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_DISCOUNT,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-discount" */
|
||||
'./cart-order-summary-discount/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_COUPON_FORM,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-coupon-form" */
|
||||
'./cart-order-summary-coupon-form/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_SHIPPING,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-shipping" */
|
||||
'./cart-order-summary-shipping/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_TAXES,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-taxes" */
|
||||
'./cart-order-summary-taxes/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
|
||||
registerCheckoutBlock( {
|
||||
metadata: metadata.CART_ORDER_SUMMARY_HEADING,
|
||||
component: lazy(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "cart-blocks/order-summary-heading" */
|
||||
'./cart-order-summary-heading/frontend'
|
||||
)
|
||||
),
|
||||
} );
|
||||
@@ -0,0 +1,344 @@
|
||||
.wc-block-cart {
|
||||
.wc-block-components-shipping-calculator {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wc-block-components-address-form {
|
||||
.wc-block-components-text-input,
|
||||
.wc-block-components-country-input,
|
||||
.wc-block-components-state-input {
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.wc-block-cart-items,
|
||||
table.wc-block-cart-items th,
|
||||
table.wc-block-cart-items td {
|
||||
// Override Storefront theme gray table background.
|
||||
background: none !important;
|
||||
// Remove borders on default themes.
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.editor-styles-wrapper table.wc-block-cart-items,
|
||||
table.wc-block-cart-items {
|
||||
width: 100%;
|
||||
|
||||
.wc-block-cart-items__header {
|
||||
@include font-size( smaller );
|
||||
text-transform: uppercase;
|
||||
|
||||
.wc-block-cart-items__header-image {
|
||||
width: 100px;
|
||||
}
|
||||
.wc-block-cart-items__header-product {
|
||||
visibility: hidden;
|
||||
}
|
||||
.wc-block-cart-items__header-total {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
.wc-block-cart-items__row {
|
||||
.wc-block-cart-item__image img {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.wc-block-cart-item__quantity {
|
||||
.wc-block-cart-item__remove-link {
|
||||
@include link-button;
|
||||
@include font-size( smaller );
|
||||
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.wc-block-components-product-name {
|
||||
display: block;
|
||||
max-width: max-content;
|
||||
}
|
||||
.wc-block-cart-item__total {
|
||||
@include font-size( regular );
|
||||
text-align: right;
|
||||
line-height: inherit;
|
||||
}
|
||||
.wc-block-components-product-metadata {
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-cart {
|
||||
.wc-block-components-totals-taxes,
|
||||
.wc-block-components-totals-footer-item {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading placeholder state.
|
||||
.wc-block-cart--is-loading,
|
||||
.wc-block-mini-cart__drawer.is-loading {
|
||||
th span,
|
||||
h2 span {
|
||||
@include placeholder();
|
||||
@include force-content();
|
||||
min-width: 84px;
|
||||
display: inline-block;
|
||||
}
|
||||
h2 span {
|
||||
min-width: 33%;
|
||||
}
|
||||
.wc-block-components-product-price,
|
||||
.wc-block-components-product-metadata,
|
||||
.wc-block-components-quantity-selector {
|
||||
@include placeholder();
|
||||
}
|
||||
.wc-block-components-product-name {
|
||||
@include placeholder();
|
||||
@include force-content();
|
||||
min-width: 84px;
|
||||
display: inline-block;
|
||||
}
|
||||
.wc-block-components-product-metadata {
|
||||
margin-top: 0.25em;
|
||||
min-width: 8em;
|
||||
}
|
||||
.wc-block-cart-item__remove-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
.wc-block-cart-item__image > a {
|
||||
@include placeholder();
|
||||
display: block;
|
||||
}
|
||||
.wc-block-components-product-price {
|
||||
@include force-content();
|
||||
max-width: 3em;
|
||||
display: block;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.wc-block-cart__sidebar .components-card {
|
||||
@include placeholder();
|
||||
@include force-content();
|
||||
min-height: 460px;
|
||||
}
|
||||
}
|
||||
.wc-block-components-sidebar-layout.wc-block-cart--skeleton {
|
||||
display: none;
|
||||
}
|
||||
.is-loading + .wc-block-components-sidebar-layout.wc-block-cart--skeleton {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.wc-block-cart-item__total-price-and-sale-badge-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
.wc-block-components-sale-badge {
|
||||
margin-top: $gap-smallest;
|
||||
}
|
||||
}
|
||||
|
||||
.is-small,
|
||||
.is-mobile {
|
||||
.wc-block-cart-item__total {
|
||||
.wc-block-components-sale-badge {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-medium,
|
||||
.is-small,
|
||||
.is-mobile {
|
||||
&.wc-block-cart {
|
||||
.wc-block-components-sidebar {
|
||||
.wc-block-cart__totals-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
table.wc-block-cart-items {
|
||||
td {
|
||||
padding: 0;
|
||||
}
|
||||
.wc-block-cart-items__header {
|
||||
display: none;
|
||||
}
|
||||
.wc-block-cart-item__remove-link {
|
||||
display: none;
|
||||
}
|
||||
&:not(.wc-block-mini-cart-items) {
|
||||
.wc-block-cart-items__row {
|
||||
@include with-translucent-border( 0 0 1px );
|
||||
}
|
||||
}
|
||||
.wc-block-cart-items__row {
|
||||
display: grid;
|
||||
grid-template-columns: 80px 132px;
|
||||
padding: $gap 0;
|
||||
|
||||
.wc-block-cart-item__image {
|
||||
grid-column-start: 1;
|
||||
grid-row-start: 1;
|
||||
padding-right: $gap;
|
||||
}
|
||||
.wc-block-cart-item__product {
|
||||
grid-column-start: 2;
|
||||
grid-column-end: 4;
|
||||
grid-row-start: 1;
|
||||
justify-self: stretch;
|
||||
padding: 0 $gap $gap 0;
|
||||
}
|
||||
.wc-block-cart-item__quantity {
|
||||
grid-column-start: 1;
|
||||
grid-row-start: 2;
|
||||
vertical-align: bottom;
|
||||
padding-right: $gap;
|
||||
align-self: end;
|
||||
padding-top: $gap;
|
||||
}
|
||||
.wc-block-cart-item__total {
|
||||
grid-row-start: 1;
|
||||
|
||||
.wc-block-components-formatted-money-amount {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-large.wc-block-cart {
|
||||
margin-bottom: 3em;
|
||||
|
||||
.wc-block-cart-items {
|
||||
@include with-translucent-border( 0 0 1px );
|
||||
|
||||
th {
|
||||
padding: 0.25rem $gap 0.25rem 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
td {
|
||||
@include with-translucent-border( 1px 0 0 );
|
||||
padding: $gap 0 $gap $gap;
|
||||
vertical-align: top;
|
||||
}
|
||||
th:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
td:last-child {
|
||||
padding-right: $gap;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-components-radio-control__input {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.wc-block-cart__totals-title {
|
||||
@include text-heading();
|
||||
@include font-size( smaller );
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
padding: 0.25rem 0;
|
||||
text-align: right;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wc-block-components-sidebar {
|
||||
.wc-block-components-shipping-calculator,
|
||||
.wc-block-components-shipping-rates-control__package:not(.wc-block-components-panel) {
|
||||
padding-left: $gap;
|
||||
padding-right: $gap;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-cart__payment-options {
|
||||
padding: $gap 0 0;
|
||||
|
||||
&:empty {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart.is-loading {
|
||||
.wp-block-woocommerce-empty-cart-block {
|
||||
display: none;
|
||||
}
|
||||
.wp-block-woocommerce-filled-cart-block {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 auto $gap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-items-block {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding-right: math.percentage(math.div($gap-largest, 1060px)); // ~1060px is the default width of the content area in Storefront.
|
||||
width: 65%;
|
||||
min-height: 10em;
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-line-items-block {
|
||||
min-height: 15em;
|
||||
display: block;
|
||||
@include placeholder();
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-totals-block {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding-left: math.percentage(math.div($gap-large, 1060px));
|
||||
width: 35%;
|
||||
min-height: 12em;
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-order-summary-block,
|
||||
.wp-block-woocommerce-cart-express-payment-block,
|
||||
.wp-block-woocommerce-proceed-to-checkout-block,
|
||||
.wp-block-woocommerce-cart-accepted-payment-methods-block {
|
||||
min-height: 3em;
|
||||
display: block;
|
||||
@include placeholder();
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-order-summary-block {
|
||||
height: 20em;
|
||||
}
|
||||
}
|
||||
|
||||
// Skeleton is shown before mobile classes are appended.
|
||||
@media only screen and ( max-width: 700px ) {
|
||||
.wp-block-woocommerce-cart.is-loading {
|
||||
padding-top: $gap;
|
||||
|
||||
.wp-block-woocommerce-filled-cart-block {
|
||||
flex-direction: column;
|
||||
margin: 0 auto $gap;
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-items-block {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wp-block-woocommerce-cart-totals-block {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user