woocommerce/plugins/woocommerce-blocks/assets/js/blocks/cart/inner-blocks/proceed-to-checkout-block/block.tsx

127 lines
3.7 KiB
TypeScript

/**
* External dependencies
*/
import classnames from 'classnames';
import { useState, useEffect, useMemo } from '@wordpress/element';
import Button from '@woocommerce/base-components/button';
import { CHECKOUT_URL } from '@woocommerce/block-settings';
import { usePositionRelativeToViewport } from '@woocommerce/base-hooks';
import { getSetting } from '@woocommerce/settings';
import { useSelect } from '@wordpress/data';
import { CART_STORE_KEY, CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { applyCheckoutFilter } from '@woocommerce/blocks-checkout';
import { isErrorResponse } from '@woocommerce/base-context';
import { useCartEventsContext } from '@woocommerce/base-context/providers';
/**
* Internal dependencies
*/
import { defaultButtonLabel } from './constants';
/**
* Checkout button rendered in the full cart page.
*/
const Block = ( {
checkoutPageId,
className,
buttonLabel,
}: {
checkoutPageId: number;
className: string;
buttonLabel: string;
} ): JSX.Element => {
const link = getSetting< string >( 'page-' + checkoutPageId, false );
const isCalculating = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).isCalculating()
);
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 cart = useSelect( ( select ) => {
return select( CART_STORE_KEY ).getCartData();
} );
const label = applyCheckoutFilter< string >( {
filterName: 'proceedToCheckoutButtonLabel',
defaultValue: buttonLabel || defaultButtonLabel,
arg: { cart },
} );
const filteredLink = applyCheckoutFilter< string >( {
filterName: 'proceedToCheckoutButtonLink',
defaultValue: link || CHECKOUT_URL,
arg: { cart },
} );
const { dispatchOnProceedToCheckout } = useCartEventsContext();
const submitContainerContents = (
<Button
className="wc-block-cart__submit-button"
href={ filteredLink }
disabled={ isCalculating }
onClick={ ( e ) => {
dispatchOnProceedToCheckout().then( ( observerResponses ) => {
if ( observerResponses.some( isErrorResponse ) ) {
e.preventDefault();
return;
}
setShowSpinner( true );
} );
} }
showSpinner={ showSpinner }
>
{ label }
</Button>
);
// Get the body background color to use as the sticky container background color.
const backgroundColor = useMemo(
() => getComputedStyle( document.body ).backgroundColor,
[]
);
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"
style={ { backgroundColor } }
>
{ submitContainerContents }
</div>
) }
</div>
);
};
export default Block;