initial commit
This commit is contained in:
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import classNames from 'classnames';
|
||||
import { _n, sprintf } from '@wordpress/i18n';
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { PackageRateOption } from '@woocommerce/type-defs/shipping';
|
||||
import { Panel } from '@woocommerce/blocks-checkout';
|
||||
import Label from '@woocommerce/base-components/label';
|
||||
import { useSelectShippingRate } from '@woocommerce/base-context/hooks';
|
||||
import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import PackageRates from './package-rates';
|
||||
import './style.scss';
|
||||
|
||||
interface PackageItem {
|
||||
name: string;
|
||||
key: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
interface Destination {
|
||||
address_1: string;
|
||||
address_2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
postcode: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
export interface PackageData {
|
||||
destination: Destination;
|
||||
name: string;
|
||||
shipping_rates: CartShippingPackageShippingRate[];
|
||||
items: PackageItem[];
|
||||
}
|
||||
|
||||
export type PackageRateRenderOption = (
|
||||
option: CartShippingPackageShippingRate
|
||||
) => PackageRateOption;
|
||||
|
||||
interface PackageProps {
|
||||
/* PackageId can be a string, WooCommerce Subscriptions uses strings for example, but WooCommerce core uses numbers */
|
||||
packageId: string | number;
|
||||
renderOption: PackageRateRenderOption;
|
||||
collapse?: boolean;
|
||||
packageData: PackageData;
|
||||
className?: string;
|
||||
collapsible?: boolean;
|
||||
noResultsMessage: ReactElement;
|
||||
showItems?: boolean;
|
||||
}
|
||||
|
||||
export const ShippingRatesControlPackage = ( {
|
||||
packageId,
|
||||
className,
|
||||
noResultsMessage,
|
||||
renderOption,
|
||||
packageData,
|
||||
collapsible = false,
|
||||
collapse = false,
|
||||
showItems = false,
|
||||
}: PackageProps ): ReactElement => {
|
||||
const { selectShippingRate, selectedShippingRate } = useSelectShippingRate(
|
||||
packageId,
|
||||
packageData.shipping_rates
|
||||
);
|
||||
|
||||
const header = (
|
||||
<>
|
||||
{ ( showItems || collapsible ) && (
|
||||
<div className="wc-block-components-shipping-rates-control__package-title">
|
||||
{ packageData.name }
|
||||
</div>
|
||||
) }
|
||||
{ showItems && (
|
||||
<ul className="wc-block-components-shipping-rates-control__package-items">
|
||||
{ Object.values( packageData.items ).map( ( v ) => {
|
||||
const name = decodeEntities( v.name );
|
||||
const quantity = v.quantity;
|
||||
return (
|
||||
<li
|
||||
key={ v.key }
|
||||
className="wc-block-components-shipping-rates-control__package-item"
|
||||
>
|
||||
<Label
|
||||
label={
|
||||
quantity > 1
|
||||
? `${ name } × ${ quantity }`
|
||||
: `${ name }`
|
||||
}
|
||||
screenReaderLabel={ sprintf(
|
||||
/* translators: %1$s name of the product (ie: Sunglasses), %2$d number of units in the current cart package */
|
||||
_n(
|
||||
'%1$s (%2$d unit)',
|
||||
'%1$s (%2$d units)',
|
||||
quantity,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
name,
|
||||
quantity
|
||||
) }
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
} ) }
|
||||
</ul>
|
||||
) }
|
||||
</>
|
||||
);
|
||||
const body = (
|
||||
<PackageRates
|
||||
className={ className }
|
||||
noResultsMessage={ noResultsMessage }
|
||||
rates={ packageData.shipping_rates }
|
||||
onSelectRate={ selectShippingRate }
|
||||
selected={ selectedShippingRate }
|
||||
renderOption={ renderOption }
|
||||
/>
|
||||
);
|
||||
if ( collapsible ) {
|
||||
return (
|
||||
<Panel
|
||||
className="wc-block-components-shipping-rates-control__package"
|
||||
initialOpen={ ! collapse }
|
||||
title={ header }
|
||||
>
|
||||
{ body }
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={ classNames(
|
||||
'wc-block-components-shipping-rates-control__package',
|
||||
className
|
||||
) }
|
||||
>
|
||||
{ header }
|
||||
{ body }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShippingRatesControlPackage;
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import RadioControl, {
|
||||
RadioControlOptionLayout,
|
||||
} from '@woocommerce/base-components/radio-control';
|
||||
import type { PackageRateOption } from '@woocommerce/type-defs/shipping';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { renderPackageRateOption } from './render-package-rate-option';
|
||||
|
||||
interface PackageRates {
|
||||
onSelectRate: ( selectedRateId: string ) => void;
|
||||
rates: CartShippingPackageShippingRate[];
|
||||
renderOption?: (
|
||||
option: CartShippingPackageShippingRate
|
||||
) => PackageRateOption;
|
||||
className?: string;
|
||||
noResultsMessage: ReactElement;
|
||||
selected?: string;
|
||||
}
|
||||
|
||||
const PackageRates = ( {
|
||||
className,
|
||||
noResultsMessage,
|
||||
onSelectRate,
|
||||
rates,
|
||||
renderOption = renderPackageRateOption,
|
||||
selected,
|
||||
}: PackageRates ): ReactElement => {
|
||||
if ( rates.length === 0 ) {
|
||||
return noResultsMessage;
|
||||
}
|
||||
|
||||
if ( rates.length > 1 ) {
|
||||
return (
|
||||
<RadioControl
|
||||
className={ className }
|
||||
onChange={ ( selectedRateId: string ) => {
|
||||
onSelectRate( selectedRateId );
|
||||
} }
|
||||
selected={ selected }
|
||||
options={ rates.map( renderOption ) }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
label,
|
||||
secondaryLabel,
|
||||
description,
|
||||
secondaryDescription,
|
||||
} = renderOption( rates[ 0 ] );
|
||||
|
||||
return (
|
||||
<RadioControlOptionLayout
|
||||
label={ label }
|
||||
secondaryLabel={ secondaryLabel }
|
||||
description={ description }
|
||||
secondaryDescription={ secondaryDescription }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PackageRates;
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { decodeEntities } from '@wordpress/html-entities';
|
||||
import { getCurrencyFromPriceResponse } from '@woocommerce/price-format';
|
||||
import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount';
|
||||
import type { PackageRateOption } from '@woocommerce/type-defs/shipping';
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import { CartShippingPackageShippingRate } from '@woocommerce/type-defs/cart';
|
||||
|
||||
/**
|
||||
* Default render function for package rate options.
|
||||
*
|
||||
* @param {Object} rate Rate data.
|
||||
*/
|
||||
export const renderPackageRateOption = (
|
||||
rate: CartShippingPackageShippingRate
|
||||
): PackageRateOption => {
|
||||
const priceWithTaxes: number = getSetting(
|
||||
'displayCartPricesIncludingTax',
|
||||
false
|
||||
)
|
||||
? parseInt( rate.price, 10 ) + parseInt( rate.taxes, 10 )
|
||||
: parseInt( rate.price, 10 );
|
||||
|
||||
return {
|
||||
label: decodeEntities( rate.name ),
|
||||
value: rate.rate_id,
|
||||
description: (
|
||||
<>
|
||||
{ Number.isFinite( priceWithTaxes ) && (
|
||||
<FormattedMonetaryAmount
|
||||
currency={ getCurrencyFromPriceResponse( rate ) }
|
||||
value={ priceWithTaxes }
|
||||
/>
|
||||
) }
|
||||
{ Number.isFinite( priceWithTaxes ) && rate.delivery_time
|
||||
? ' — '
|
||||
: null }
|
||||
{ decodeEntities( rate.delivery_time ) }
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
export default renderPackageRateOption;
|
@ -0,0 +1,43 @@
|
||||
.wc-block-components-shipping-rates-control__package {
|
||||
.wc-block-components-panel__button {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
padding-bottom: em($gap-small);
|
||||
padding-top: em($gap-small);
|
||||
}
|
||||
|
||||
// Remove panel padding because we are adding bottom padding to `.wc-block-components-radio-control`
|
||||
// and `.wc-block-components-radio-control__option-layout` in the next ruleset.
|
||||
.wc-block-components-panel__content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.wc-block-components-radio-control,
|
||||
.wc-block-components-radio-control__option-layout {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.wc-block-components-radio-control .wc-block-components-radio-control__option-layout {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wc-block-components-shipping-rates-control__package-items {
|
||||
@include font-size(small);
|
||||
display: block;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wc-block-components-shipping-rates-control__package-item {
|
||||
@include wrap-break-word();
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wc-block-components-shipping-rates-control__package-item:not(:last-child)::after {
|
||||
content: ", ";
|
||||
white-space: pre;
|
||||
}
|
Reference in New Issue
Block a user