initial commit
This commit is contained in:
@ -0,0 +1,309 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
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 { 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/>' );
|
||||
|
||||
/**
|
||||
* Cart line item table row component.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {CartItem|Object} props.lineItem
|
||||
*/
|
||||
const CartLineItemRow = ( {
|
||||
lineItem,
|
||||
}: {
|
||||
lineItem: CartItem | Record< string, never >;
|
||||
} ): 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_limit: quantityLimit = 99,
|
||||
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';
|
||||
|
||||
// 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', {
|
||||
'is-disabled': isPendingDelete,
|
||||
} ) }
|
||||
>
|
||||
{ /* 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 } />
|
||||
) : (
|
||||
<a href={ permalink } tabIndex={ -1 }>
|
||||
<ProductImage image={ firstImage } />
|
||||
</a>
|
||||
) }
|
||||
</td>
|
||||
<td className="wc-block-cart-item__product">
|
||||
<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">
|
||||
<QuantitySelector
|
||||
disabled={ isPendingDelete }
|
||||
quantity={ quantity }
|
||||
maximum={ quantityLimit }
|
||||
onChange={ ( newQuantity ) => {
|
||||
setItemQuantity( newQuantity );
|
||||
dispatchStoreEvent( 'cart-set-item-quantity', {
|
||||
product: lineItem,
|
||||
quantity: newQuantity,
|
||||
} );
|
||||
} }
|
||||
itemName={ name }
|
||||
/>
|
||||
<button
|
||||
className="wc-block-cart-item__remove-link"
|
||||
onClick={ () => {
|
||||
removeItem();
|
||||
dispatchStoreEvent( 'cart-remove-item', {
|
||||
product: lineItem,
|
||||
quantity,
|
||||
} );
|
||||
} }
|
||||
disabled={ isPendingDelete }
|
||||
>
|
||||
{ __( 'Remove item', 'woo-gutenberg-products-block' ) }
|
||||
</button>
|
||||
</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,62 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { CartResponseItem } from '@woocommerce/type-defs/cart-response';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
const CartLineItemsTable = ( {
|
||||
lineItems = [],
|
||||
isLoading = false,
|
||||
}: CartLineItemsTableProps ): JSX.Element => {
|
||||
const products = isLoading
|
||||
? placeholderRows
|
||||
: lineItems.map( ( lineItem ) => {
|
||||
return (
|
||||
<CartLineItemRow
|
||||
key={ lineItem.key }
|
||||
lineItem={ lineItem }
|
||||
/>
|
||||
);
|
||||
} );
|
||||
|
||||
return (
|
||||
<table className="wc-block-cart-items">
|
||||
<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,28 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { _n, sprintf } from '@wordpress/i18n';
|
||||
import Title from '@woocommerce/base-components/title';
|
||||
|
||||
const CartLineItemsTitle = ( {
|
||||
itemCount = 1,
|
||||
}: {
|
||||
itemCount: number;
|
||||
} ): JSX.Element => {
|
||||
return (
|
||||
<Title headingLevel="2">
|
||||
{ sprintf(
|
||||
/* translators: %d is the count of items in the cart. */
|
||||
_n(
|
||||
'Your cart (%d item)',
|
||||
'Your cart (%d items)',
|
||||
itemCount,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
itemCount
|
||||
) }
|
||||
</Title>
|
||||
);
|
||||
};
|
||||
|
||||
export default CartLineItemsTitle;
|
@ -0,0 +1,212 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
TotalsCoupon,
|
||||
TotalsDiscount,
|
||||
TotalsFooterItem,
|
||||
TotalsShipping,
|
||||
} from '@woocommerce/base-components/cart-checkout';
|
||||
import {
|
||||
Subtotal,
|
||||
TotalsFees,
|
||||
TotalsTaxes,
|
||||
TotalsWrapper,
|
||||
ExperimentalOrderMeta,
|
||||
ExperimentalDiscountsMeta,
|
||||
} from '@woocommerce/blocks-checkout';
|
||||
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
|
||||
import {
|
||||
useStoreCartCoupons,
|
||||
useStoreCart,
|
||||
useStoreNotices,
|
||||
} from '@woocommerce/base-context/hooks';
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarLayout,
|
||||
Main,
|
||||
} from '@woocommerce/base-components/sidebar-layout';
|
||||
import Title from '@woocommerce/base-components/title';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import { useEffect } from '@wordpress/element';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CheckoutButton from '../checkout-button';
|
||||
import CartLineItemsTitle from './cart-line-items-title';
|
||||
import CartLineItemsTable from './cart-line-items-table';
|
||||
import { CartExpressPayment } from '../../payment-methods';
|
||||
import './style.scss';
|
||||
|
||||
interface CartAttributes {
|
||||
hasDarkControls: boolean;
|
||||
isShippingCalculatorEnabled: boolean;
|
||||
checkoutPageId: number;
|
||||
isPreview: boolean;
|
||||
showRateAfterTaxName: boolean;
|
||||
}
|
||||
|
||||
interface CartProps {
|
||||
attributes: CartAttributes;
|
||||
}
|
||||
/**
|
||||
* Component that renders the Cart block when user has something in cart aka "full".
|
||||
*
|
||||
* @param {Object} props Incoming props for the component.
|
||||
* @param {Object} props.attributes Incoming attributes for block.
|
||||
*/
|
||||
const Cart = ( { attributes }: CartProps ): JSX.Element => {
|
||||
const {
|
||||
isShippingCalculatorEnabled,
|
||||
hasDarkControls,
|
||||
showRateAfterTaxName,
|
||||
} = attributes;
|
||||
|
||||
const {
|
||||
cartItems,
|
||||
cartFees,
|
||||
cartTotals,
|
||||
cartIsLoading,
|
||||
cartItemsCount,
|
||||
cartItemErrors,
|
||||
cartNeedsPayment,
|
||||
cartNeedsShipping,
|
||||
} = useStoreCart();
|
||||
|
||||
const {
|
||||
applyCoupon,
|
||||
removeCoupon,
|
||||
isApplyingCoupon,
|
||||
isRemovingCoupon,
|
||||
appliedCoupons,
|
||||
} = useStoreCartCoupons();
|
||||
|
||||
const { addErrorNotice } = useStoreNotices();
|
||||
|
||||
// Ensures any cart errors listed in the API response get shown.
|
||||
useEffect( () => {
|
||||
cartItemErrors.forEach( ( error ) => {
|
||||
addErrorNotice( decodeEntities( error.message ), {
|
||||
isDismissible: true,
|
||||
id: error.code,
|
||||
} );
|
||||
} );
|
||||
}, [ addErrorNotice, cartItemErrors ] );
|
||||
|
||||
const totalsCurrency = getCurrencyFromPriceResponse( cartTotals );
|
||||
|
||||
const cartClassName = classnames( 'wc-block-cart', {
|
||||
'wc-block-cart--is-loading': cartIsLoading,
|
||||
'has-dark-controls': hasDarkControls,
|
||||
} );
|
||||
|
||||
// Prepare props to pass to the ExperimentalOrderMeta slot fill.
|
||||
// We need to pluck out receiveCart.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { extensions, ...cart } = useStoreCart();
|
||||
const slotFillProps = {
|
||||
extensions,
|
||||
cart,
|
||||
};
|
||||
|
||||
const discountsSlotFillProps = {
|
||||
extensions,
|
||||
cart,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<CartLineItemsTitle itemCount={ cartItemsCount } />
|
||||
<SidebarLayout className={ cartClassName }>
|
||||
<Main className="wc-block-cart__main">
|
||||
<CartLineItemsTable
|
||||
lineItems={ cartItems }
|
||||
isLoading={ cartIsLoading }
|
||||
/>
|
||||
</Main>
|
||||
<Sidebar className="wc-block-cart__sidebar">
|
||||
<Title
|
||||
headingLevel="2"
|
||||
className="wc-block-cart__totals-title"
|
||||
>
|
||||
{ __( 'Cart totals', 'woo-gutenberg-products-block' ) }
|
||||
</Title>
|
||||
<TotalsWrapper>
|
||||
<Subtotal
|
||||
currency={ totalsCurrency }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
<TotalsFees
|
||||
currency={ totalsCurrency }
|
||||
cartFees={ cartFees }
|
||||
/>
|
||||
<TotalsDiscount
|
||||
cartCoupons={ appliedCoupons }
|
||||
currency={ totalsCurrency }
|
||||
isRemovingCoupon={ isRemovingCoupon }
|
||||
removeCoupon={ removeCoupon }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
{ getSetting( 'couponsEnabled', true ) && (
|
||||
<TotalsWrapper>
|
||||
<TotalsCoupon
|
||||
onSubmit={ applyCoupon }
|
||||
isLoading={ isApplyingCoupon }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
) }
|
||||
<ExperimentalDiscountsMeta.Slot
|
||||
{ ...discountsSlotFillProps }
|
||||
/>
|
||||
{ cartNeedsShipping && (
|
||||
<TotalsWrapper>
|
||||
<TotalsShipping
|
||||
showCalculator={ isShippingCalculatorEnabled }
|
||||
showRateSelector={ true }
|
||||
values={ cartTotals }
|
||||
currency={ totalsCurrency }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
) }
|
||||
{ ! getSetting( 'displayCartPricesIncludingTax', false ) &&
|
||||
parseInt( cartTotals.total_tax, 10 ) > 0 && (
|
||||
<TotalsWrapper>
|
||||
<TotalsTaxes
|
||||
showRateAfterTaxName={
|
||||
showRateAfterTaxName
|
||||
}
|
||||
currency={ totalsCurrency }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
) }
|
||||
<TotalsWrapper>
|
||||
<TotalsFooterItem
|
||||
currency={ totalsCurrency }
|
||||
values={ cartTotals }
|
||||
/>
|
||||
</TotalsWrapper>
|
||||
|
||||
<ExperimentalOrderMeta.Slot { ...slotFillProps } />
|
||||
|
||||
<div className="wc-block-cart__payment-options">
|
||||
{ cartNeedsPayment && <CartExpressPayment /> }
|
||||
<CheckoutButton
|
||||
link={ getSetting(
|
||||
'page-' + attributes?.checkoutPageId,
|
||||
false
|
||||
) }
|
||||
/>
|
||||
</div>
|
||||
</Sidebar>
|
||||
</SidebarLayout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Cart;
|
@ -0,0 +1,264 @@
|
||||
.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;
|
||||
}
|
||||
.wc-block-cart-items__row {
|
||||
@include with-translucent-border(0 0 1px);
|
||||
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 {
|
||||
.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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user