Add Mini Cart block in experimental builds (https://github.com/woocommerce/woocommerce-blocks/pull/4510)
* Create MiniCart block prototype * Use window.onload instead of DOMContentLoaded * Don't load translations for scripts without localized strings * Don't try to load cart instance in API requests * Remove PHP pre-rendering * Fix some typos * Move Mini Cart block files under 'cart-checkout' directory * Update selectors to follow guidelines * Only enable the MiniCart block in experimental builds * Fix wrong translations element selector * Improve lazyLoadScript and preloadScript documentation * Move lazy-load-script and preload-script to base-utils * Add function to check if script tag is already in the DOM
This commit is contained in:
parent
ea55000792
commit
2b2631d7ab
|
@ -0,0 +1,82 @@
|
|||
interface lazyLoadScriptParams {
|
||||
handle: string;
|
||||
src: string;
|
||||
version?: string;
|
||||
after?: string;
|
||||
before?: string;
|
||||
translations?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* In WP, registered scripts are loaded into the page with an element like this:
|
||||
* `<script src='...' id='[SCRIPT_ID]'></script>`
|
||||
* This function checks whether an element matching that selector exists.
|
||||
* Useful to know if a script has already been appended to the page.
|
||||
*/
|
||||
const isScriptTagInDOM = ( scriptId: string ): boolean => {
|
||||
const scriptElements = document.querySelectorAll( `script#${ scriptId }` );
|
||||
return scriptElements.length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Appends a `<script>` tag to the document body based on the src and handle
|
||||
* parameters. In addition, it appends additional script tags to load the code
|
||||
* needed for translations and any before and after inline scripts. See these
|
||||
* documentation pages for more information:
|
||||
*
|
||||
* https://developer.wordpress.org/reference/functions/wp_set_script_translations/
|
||||
* https://developer.wordpress.org/reference/functions/wp_add_inline_script/
|
||||
*/
|
||||
const lazyLoadScript = ( {
|
||||
handle,
|
||||
src,
|
||||
version,
|
||||
after,
|
||||
before,
|
||||
translations,
|
||||
}: lazyLoadScriptParams ): Promise< void > => {
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
// Append script translations if they doesn't exist yet in the page.
|
||||
if (
|
||||
translations &&
|
||||
! isScriptTagInDOM( `${ handle }-js-translations` )
|
||||
) {
|
||||
const handleTranslations = document.createElement( 'script' );
|
||||
handleTranslations.innerHTML = translations;
|
||||
handleTranslations.id = `${ handle }-js-translations`;
|
||||
document.body.appendChild( handleTranslations );
|
||||
}
|
||||
// Append before inline script if it doesn't exist yet in the page.
|
||||
if ( before && ! isScriptTagInDOM( `${ handle }-js-before` ) ) {
|
||||
const handleBeforeScript = document.createElement( 'script' );
|
||||
handleBeforeScript.innerHTML = before;
|
||||
handleBeforeScript.id = `${ handle }-js-before`;
|
||||
document.body.appendChild( handleBeforeScript );
|
||||
}
|
||||
|
||||
if ( isScriptTagInDOM( `${ handle }-js` ) ) {
|
||||
resolve();
|
||||
} else {
|
||||
// Append script.
|
||||
const handleScript = document.createElement( 'script' );
|
||||
handleScript.src = version ? `${ src }?${ version }` : src;
|
||||
handleScript.id = `${ handle }-js`;
|
||||
handleScript.onerror = reject;
|
||||
handleScript.onload = () => {
|
||||
// Append after inline script if it doesn't exist yet in the page.
|
||||
if ( after && ! isScriptTagInDOM( `${ handle }-js-after` ) ) {
|
||||
const handleAfterScript = document.createElement(
|
||||
'script'
|
||||
);
|
||||
handleAfterScript.innerHTML = after;
|
||||
handleAfterScript.id = `${ handle }-js-after`;
|
||||
document.body.appendChild( handleAfterScript );
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
document.body.appendChild( handleScript );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
export default lazyLoadScript;
|
|
@ -0,0 +1,30 @@
|
|||
interface preloadScriptParams {
|
||||
handle: string;
|
||||
src: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a `<link>` tag to the document head to preload a script based on the
|
||||
* src and handle parameters.
|
||||
*/
|
||||
const preloadScript = ( {
|
||||
handle,
|
||||
src,
|
||||
version,
|
||||
}: preloadScriptParams ): void => {
|
||||
const handleScriptElements = document.querySelectorAll(
|
||||
`#${ handle }-js, #${ handle }-js-prefetch`
|
||||
);
|
||||
|
||||
if ( handleScriptElements.length === 0 ) {
|
||||
const prefetchLink = document.createElement( 'link' );
|
||||
prefetchLink.href = version ? `${ src }?${ version }` : src;
|
||||
prefetchLink.rel = 'preload';
|
||||
prefetchLink.as = 'script';
|
||||
prefetchLink.id = `${ handle }-js-prefetch`;
|
||||
document.head.appendChild( prefetchLink );
|
||||
}
|
||||
};
|
||||
|
||||
export default preloadScript;
|
|
@ -36,8 +36,8 @@ const isElementInsideWrappers = ( el, wrappers ) => {
|
|||
const renderBlockInContainers = ( {
|
||||
Block,
|
||||
containers,
|
||||
getProps = () => {},
|
||||
getErrorBoundaryProps = () => {},
|
||||
getProps = () => ( {} ),
|
||||
getErrorBoundaryProps = () => ( {} ),
|
||||
} ) => {
|
||||
if ( containers.length === 0 ) {
|
||||
return;
|
||||
|
@ -49,7 +49,7 @@ const renderBlockInContainers = ( {
|
|||
const errorBoundaryProps = getErrorBoundaryProps( el, i );
|
||||
const attributes = {
|
||||
...el.dataset,
|
||||
...props.attributes,
|
||||
...( props.attributes || {} ),
|
||||
};
|
||||
el.classList.remove( 'is-loading' );
|
||||
|
||||
|
|
|
@ -86,7 +86,8 @@ table.wc-block-cart-items {
|
|||
}
|
||||
|
||||
// Loading placeholder state.
|
||||
.wc-block-cart--is-loading {
|
||||
.wc-block-cart--is-loading,
|
||||
.wc-block-mini-cart-items--is-loading {
|
||||
th span,
|
||||
h2 span {
|
||||
@include placeholder();
|
||||
|
@ -97,47 +98,43 @@ table.wc-block-cart-items {
|
|||
h2 span {
|
||||
min-width: 33%;
|
||||
}
|
||||
.wc-block-cart-items {
|
||||
.wc-block-cart-items__row {
|
||||
.wc-block-cart-item__price,
|
||||
.wc-block-cart-item__individual-price,
|
||||
.wc-block-cart-item__product-metadata,
|
||||
.wc-block-cart-item__image > *,
|
||||
.wc-block-components-quantity-selector {
|
||||
@include placeholder();
|
||||
}
|
||||
.wc-block-cart-item__product-name {
|
||||
@include placeholder();
|
||||
@include force-content();
|
||||
min-width: 84px;
|
||||
display: inline-block;
|
||||
}
|
||||
.wc-block-cart-item__product-metadata {
|
||||
margin-top: 0.25em;
|
||||
min-width: 8em;
|
||||
}
|
||||
.wc-block-cart-item__remove-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
.wc-block-cart-item__image a {
|
||||
display: block;
|
||||
}
|
||||
.wc-block-cart-item__individual-price {
|
||||
@include force-content();
|
||||
max-width: 3em;
|
||||
display: block;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.wc-block-cart-item__total {
|
||||
> span,
|
||||
> div {
|
||||
display: none;
|
||||
}
|
||||
.wc-block-cart-item__price {
|
||||
@include force-content();
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.wc-block-cart-item__price,
|
||||
.wc-block-cart-item__individual-price,
|
||||
.wc-block-cart-item__product-metadata,
|
||||
.wc-block-cart-item__image > *,
|
||||
.wc-block-components-quantity-selector {
|
||||
@include placeholder();
|
||||
}
|
||||
.wc-block-cart-item__product-name {
|
||||
@include placeholder();
|
||||
@include force-content();
|
||||
min-width: 84px;
|
||||
display: inline-block;
|
||||
}
|
||||
.wc-block-cart-item__product-metadata {
|
||||
margin-top: 0.25em;
|
||||
min-width: 8em;
|
||||
}
|
||||
.wc-block-cart-item__remove-link {
|
||||
visibility: hidden;
|
||||
}
|
||||
.wc-block-cart-item__image a {
|
||||
display: block;
|
||||
}
|
||||
.wc-block-cart-item__individual-price {
|
||||
@include force-content();
|
||||
max-width: 3em;
|
||||
display: block;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.wc-block-cart-item__total {
|
||||
> span,
|
||||
> div {
|
||||
display: none;
|
||||
}
|
||||
.wc-block-cart-item__price {
|
||||
@include force-content();
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.wc-block-cart__sidebar .components-card {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { renderFrontend } from '@woocommerce/base-utils';
|
||||
import { useStoreCart } from '@woocommerce/base-context/hooks';
|
||||
import {
|
||||
withStoreCartApiHydration,
|
||||
withRestApiHydration,
|
||||
} from '@woocommerce/block-hocs';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import CartLineItemsTable from '../cart/full-cart/cart-line-items-table';
|
||||
|
||||
const MiniCartContents = () => {
|
||||
const { cartItems, cartIsLoading } = useStoreCart();
|
||||
|
||||
if ( cartItems.length === 0 ) {
|
||||
return <>{ __( 'Cart is empty', 'woo-gutenberg-products-block' ) }</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<CartLineItemsTable
|
||||
lineItems={ cartItems }
|
||||
isLoading={ cartIsLoading }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderFrontend( {
|
||||
selector: '.wc-block-mini-cart__contents',
|
||||
Block: withStoreCartApiHydration(
|
||||
withRestApiHydration( MiniCartContents )
|
||||
),
|
||||
} );
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { _n, sprintf } from '@wordpress/i18n';
|
||||
import { useBlockProps } from '@wordpress/block-editor';
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
const MiniCartBlock = (): ReactElement => {
|
||||
const blockProps = useBlockProps( {
|
||||
className: 'wc-block-mini-cart',
|
||||
} );
|
||||
|
||||
const productCount = 0;
|
||||
|
||||
return (
|
||||
<div { ...blockProps }>
|
||||
<button className="wc-block-mini-cart__button">
|
||||
{ sprintf(
|
||||
/* translators: %d is the number of products in the cart. */
|
||||
_n(
|
||||
'%d product',
|
||||
'%d products',
|
||||
productCount,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
productCount
|
||||
) }
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MiniCartBlock;
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { getSetting } from '@woocommerce/settings';
|
||||
import preloadScript from '@woocommerce/base-utils/preload-script';
|
||||
import lazyLoadScript from '@woocommerce/base-utils/lazy-load-script';
|
||||
|
||||
interface dependencyData {
|
||||
src: string;
|
||||
version?: string;
|
||||
after?: string;
|
||||
before?: string;
|
||||
translations?: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @wordpress/no-global-event-listener
|
||||
window.onload = () => {
|
||||
const miniCartBlocks = document.querySelectorAll( '.wc-block-mini-cart' );
|
||||
|
||||
if ( miniCartBlocks.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dependencies = getSetting(
|
||||
'mini_cart_block_frontend_dependencies',
|
||||
{}
|
||||
) as Record< string, dependencyData >;
|
||||
|
||||
// Preload scripts
|
||||
for ( const dependencyHandle in dependencies ) {
|
||||
const dependency = dependencies[ dependencyHandle ];
|
||||
preloadScript( {
|
||||
handle: dependencyHandle,
|
||||
...dependency,
|
||||
} );
|
||||
}
|
||||
|
||||
miniCartBlocks.forEach( ( miniCartBlock ) => {
|
||||
const miniCartButton = miniCartBlock.querySelector(
|
||||
'.wc-block-mini-cart__button'
|
||||
);
|
||||
const miniCartContents = miniCartBlock.querySelector(
|
||||
'.wc-block-mini-cart__contents'
|
||||
);
|
||||
|
||||
if ( ! miniCartButton || ! miniCartContents ) {
|
||||
// Markup is not correct, abort.
|
||||
return;
|
||||
}
|
||||
|
||||
const showContents = async () => {
|
||||
miniCartContents.removeAttribute( 'hidden' );
|
||||
|
||||
// Load scripts
|
||||
for ( const dependencyHandle in dependencies ) {
|
||||
const dependency = dependencies[ dependencyHandle ];
|
||||
await lazyLoadScript( {
|
||||
handle: dependencyHandle,
|
||||
...dependency,
|
||||
} );
|
||||
}
|
||||
};
|
||||
const hideContents = () =>
|
||||
miniCartContents.setAttribute( 'hidden', 'true' );
|
||||
|
||||
miniCartButton.addEventListener( 'mouseover', showContents );
|
||||
miniCartButton.addEventListener( 'mouseleave', hideContents );
|
||||
|
||||
miniCartContents.addEventListener( 'mouseover', showContents );
|
||||
miniCartContents.addEventListener( 'mouseleave', hideContents );
|
||||
|
||||
miniCartButton.addEventListener( 'focus', showContents );
|
||||
miniCartButton.addEventListener( 'blur', hideContents );
|
||||
} );
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { Icon, cart } from '@woocommerce/icons';
|
||||
import { registerFeaturePluginBlockType } from '@woocommerce/block-settings';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import edit from './edit';
|
||||
|
||||
const settings = {
|
||||
apiVersion: 2,
|
||||
title: __( 'Mini Cart', 'woo-gutenberg-products-block' ),
|
||||
icon: {
|
||||
src: <Icon srcElement={ cart } />,
|
||||
foreground: '#96588a',
|
||||
},
|
||||
category: 'woocommerce',
|
||||
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
|
||||
description: __(
|
||||
'Display a mini cart widget.',
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
supports: {
|
||||
html: false,
|
||||
multiple: false,
|
||||
},
|
||||
example: {
|
||||
attributes: {
|
||||
isPreview: true,
|
||||
},
|
||||
},
|
||||
attributes: {
|
||||
isPreview: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
save: false,
|
||||
},
|
||||
},
|
||||
|
||||
edit,
|
||||
|
||||
save() {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
registerFeaturePluginBlockType( 'woocommerce/mini-cart', settings );
|
|
@ -50,6 +50,9 @@ const blocks = {
|
|||
customDir: 'cart-checkout/checkout-i2',
|
||||
isExperimental: true,
|
||||
},
|
||||
'mini-cart': {
|
||||
customDir: 'cart-checkout/mini-cart',
|
||||
},
|
||||
'single-product': {
|
||||
isExperimental: true,
|
||||
},
|
||||
|
@ -127,6 +130,8 @@ const entries = {
|
|||
frontend: {
|
||||
reviews: './assets/js/blocks/reviews/frontend.js',
|
||||
...getBlockEntries( 'frontend.{t,j}s{,x}' ),
|
||||
'mini-cart-component':
|
||||
'./assets/js/blocks/cart-checkout/mini-cart/component-frontend.tsx',
|
||||
},
|
||||
payments: {
|
||||
'wc-payment-method-stripe':
|
||||
|
|
|
@ -61,23 +61,14 @@ class Api {
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers a script according to `wp_register_script`, adding the correct prefix, and additionally loading translations.
|
||||
* Get src, version and dependencies given a script relative src.
|
||||
*
|
||||
* When creating script assets, the following rules should be followed:
|
||||
* 1. All asset handles should have a `wc-` prefix.
|
||||
* 2. If the asset handle is for a Block (in editor context) use the `-block` suffix.
|
||||
* 3. If the asset handle is for a Block (in frontend context) use the `-block-frontend` suffix.
|
||||
* 4. If the asset is for any other script being consumed or enqueued by the blocks plugin, use the `wc-blocks-` prefix.
|
||||
* @param string $relative_src Relative src to the script.
|
||||
* @param array $dependencies Optional. An array of registered script handles this script depends on. Default empty array.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @throws Exception If the registered script has a dependency on itself.
|
||||
*
|
||||
* @param string $handle Unique name of the script.
|
||||
* @param string $relative_src Relative url for the script to the path from plugin root.
|
||||
* @param array $dependencies Optional. An array of registered script handles this script depends on. Default empty array.
|
||||
* @param bool $has_i18n Optional. Whether to add a script translation call to this file. Default: true.
|
||||
* @return array src, version and dependencies of the script.
|
||||
*/
|
||||
public function register_script( $handle, $relative_src, $dependencies = [], $has_i18n = true ) {
|
||||
public function get_script_data( $relative_src, $dependencies = [] ) {
|
||||
$src = '';
|
||||
$version = '1';
|
||||
|
||||
|
@ -96,9 +87,36 @@ class Api {
|
|||
}
|
||||
}
|
||||
|
||||
if ( in_array( $handle, $dependencies, true ) ) {
|
||||
return array(
|
||||
'src' => $src,
|
||||
'version' => $version,
|
||||
'dependencies' => $dependencies,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a script according to `wp_register_script`, adding the correct prefix, and additionally loading translations.
|
||||
*
|
||||
* When creating script assets, the following rules should be followed:
|
||||
* 1. All asset handles should have a `wc-` prefix.
|
||||
* 2. If the asset handle is for a Block (in editor context) use the `-block` suffix.
|
||||
* 3. If the asset handle is for a Block (in frontend context) use the `-block-frontend` suffix.
|
||||
* 4. If the asset is for any other script being consumed or enqueued by the blocks plugin, use the `wc-blocks-` prefix.
|
||||
*
|
||||
* @since 2.5.0
|
||||
* @throws Exception If the registered script has a dependency on itself.
|
||||
*
|
||||
* @param string $handle Unique name of the script.
|
||||
* @param string $relative_src Relative url for the script to the path from plugin root.
|
||||
* @param array $dependencies Optional. An array of registered script handles this script depends on. Default empty array.
|
||||
* @param bool $has_i18n Optional. Whether to add a script translation call to this file. Default: true.
|
||||
*/
|
||||
public function register_script( $handle, $relative_src, $dependencies = [], $has_i18n = true ) {
|
||||
$script_data = $this->get_script_data( $relative_src, $dependencies );
|
||||
|
||||
if ( in_array( $handle, $script_data['dependencies'], true ) ) {
|
||||
if ( $this->package->feature()->is_development_environment() ) {
|
||||
$dependencies = array_diff( $dependencies, [ $handle ] );
|
||||
$dependencies = array_diff( $script_data['dependencies'], [ $handle ] );
|
||||
add_action(
|
||||
'admin_notices',
|
||||
function() use ( $handle ) {
|
||||
|
@ -113,7 +131,7 @@ class Api {
|
|||
}
|
||||
}
|
||||
|
||||
wp_register_script( $handle, $src, apply_filters( 'woocommerce_blocks_register_script_dependencies', $dependencies, $handle ), $version, true );
|
||||
wp_register_script( $handle, $script_data['src'], apply_filters( 'woocommerce_blocks_register_script_dependencies', $script_data['dependencies'], $handle ), $script_data['version'], true );
|
||||
|
||||
if ( $has_i18n && function_exists( 'wp_set_script_translations' ) ) {
|
||||
wp_set_script_translations( $handle, 'woo-gutenberg-products-block', $this->package->get_path( 'languages' ) );
|
||||
|
|
|
@ -121,23 +121,31 @@ abstract class AbstractBlock {
|
|||
*/
|
||||
protected function register_block_type_assets() {
|
||||
if ( null !== $this->get_block_type_editor_script() ) {
|
||||
$data = $this->asset_api->get_script_data( $this->get_block_type_editor_script( 'path' ) );
|
||||
$has_i18n = in_array( 'wp-i18n', $data['dependencies'], true );
|
||||
|
||||
$this->asset_api->register_script(
|
||||
$this->get_block_type_editor_script( 'handle' ),
|
||||
$this->get_block_type_editor_script( 'path' ),
|
||||
array_merge(
|
||||
$this->get_block_type_editor_script( 'dependencies' ),
|
||||
$this->integration_registry->get_all_registered_editor_script_handles()
|
||||
)
|
||||
),
|
||||
$has_i18n
|
||||
);
|
||||
}
|
||||
if ( null !== $this->get_block_type_script() ) {
|
||||
$data = $this->asset_api->get_script_data( $this->get_block_type_script( 'path' ) );
|
||||
$has_i18n = in_array( 'wp-i18n', $data['dependencies'], true );
|
||||
|
||||
$this->asset_api->register_script(
|
||||
$this->get_block_type_script( 'handle' ),
|
||||
$this->get_block_type_script( 'path' ),
|
||||
array_merge(
|
||||
$this->get_block_type_script( 'dependencies' ),
|
||||
$this->integration_registry->get_all_registered_script_handles()
|
||||
)
|
||||
),
|
||||
$has_i18n
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
namespace Automattic\WooCommerce\Blocks\BlockTypes;
|
||||
|
||||
use Automattic\WooCommerce\Blocks\Package;
|
||||
use Automattic\WooCommerce\Blocks\Assets;
|
||||
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
|
||||
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
|
||||
|
||||
/**
|
||||
* Mini Cart class.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MiniCart extends AbstractBlock {
|
||||
/**
|
||||
* Block name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $block_name = 'mini-cart';
|
||||
|
||||
/**
|
||||
* Array of scripts that will be lazy loaded when interacting with the block.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $scripts_to_lazy_load = array();
|
||||
|
||||
/**
|
||||
* Get the editor script handle for this block type.
|
||||
*
|
||||
* @param string $key Data to get, or default to everything.
|
||||
* @return array|string;
|
||||
*/
|
||||
protected function get_block_type_editor_script( $key = null ) {
|
||||
$script = [
|
||||
'handle' => 'wc-' . $this->block_name . '-block',
|
||||
'path' => $this->asset_api->get_block_asset_build_path( $this->block_name ),
|
||||
'dependencies' => [ 'wc-blocks' ],
|
||||
];
|
||||
return $key ? $script[ $key ] : $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the frontend script handle for this block type.
|
||||
*
|
||||
* @see $this->register_block_type()
|
||||
* @param string $key Data to get, or default to everything.
|
||||
* @return array|string
|
||||
*/
|
||||
protected function get_block_type_script( $key = null ) {
|
||||
$script = [
|
||||
'handle' => 'wc-' . $this->block_name . '-block-frontend',
|
||||
'path' => $this->asset_api->get_block_asset_build_path( $this->block_name . '-frontend' ),
|
||||
'dependencies' => [],
|
||||
];
|
||||
return $key ? $script[ $key ] : $script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra data passed through from server to client for block.
|
||||
*
|
||||
* @param array $attributes Any attributes that currently are available from the block.
|
||||
* Note, this will be empty in the editor context when the block is
|
||||
* not in the post content on editor load.
|
||||
*/
|
||||
protected function enqueue_data( array $attributes = [] ) {
|
||||
parent::enqueue_data( $attributes );
|
||||
|
||||
// Hydrate the following data depending on admin or frontend context.
|
||||
if ( ! is_admin() && ! WC()->is_rest_api_request() ) {
|
||||
$this->hydrate_from_api();
|
||||
}
|
||||
|
||||
$script_data = $this->asset_api->get_script_data( 'build/mini-cart-component-frontend.js' );
|
||||
|
||||
$num_dependencies = count( $script_data['dependencies'] );
|
||||
$wp_scripts = wp_scripts();
|
||||
|
||||
for ( $i = 0; $i < $num_dependencies; $i++ ) {
|
||||
$dependency = $script_data['dependencies'][ $i ];
|
||||
|
||||
foreach ( $wp_scripts->registered as $script ) {
|
||||
if ( $script->handle === $dependency ) {
|
||||
$this->append_script_and_deps_src( $script );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->scripts_to_lazy_load['wc-block-mini-cart-component-frontend'] = array(
|
||||
'src' => $script_data['src'],
|
||||
'version' => $script_data['version'],
|
||||
);
|
||||
|
||||
$this->asset_data_registry->add(
|
||||
'mini_cart_block_frontend_dependencies',
|
||||
$this->scripts_to_lazy_load,
|
||||
true
|
||||
);
|
||||
|
||||
do_action( 'woocommerce_blocks_cart_enqueue_data' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate the cart block with data from the API.
|
||||
*/
|
||||
protected function hydrate_from_api() {
|
||||
$this->asset_data_registry->hydrate_api_request( '/wc/store/cart' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the script data given its handle.
|
||||
*
|
||||
* @param string $handle Handle of the script.
|
||||
*
|
||||
* @return array Array containing the script data.
|
||||
*/
|
||||
protected function get_script_from_handle( $handle ) {
|
||||
$wp_scripts = wp_scripts();
|
||||
foreach ( $wp_scripts->registered as $script ) {
|
||||
if ( $script->handle === $handle ) {
|
||||
return $script;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively appends a scripts and its dependencies into the
|
||||
* scripts_to_lazy_load array.
|
||||
*
|
||||
* @param string $script Array containing script data.
|
||||
*/
|
||||
protected function append_script_and_deps_src( $script ) {
|
||||
$wp_scripts = wp_scripts();
|
||||
// This script and its dependencies have already been appended.
|
||||
if ( array_key_exists( $script->handle, $this->scripts_to_lazy_load ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( count( $script->deps ) > 0 ) {
|
||||
foreach ( $script->deps as $dep ) {
|
||||
if ( ! array_key_exists( $dep, $this->scripts_to_lazy_load ) ) {
|
||||
$dep_script = $this->get_script_from_handle( $dep );
|
||||
$this->append_script_and_deps_src( $dep_script );
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->scripts_to_lazy_load[ $script->handle ] = array(
|
||||
'src' => $script->src,
|
||||
'version' => $script->ver,
|
||||
'before' => $wp_scripts->print_inline_script( $script->handle, 'before', false ),
|
||||
'after' => $wp_scripts->print_inline_script( $script->handle, 'after', false ),
|
||||
'translations' => $wp_scripts->print_translations( $script->handle, false ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append frontend scripts when rendering the Mini Cart block.
|
||||
*
|
||||
* @param array $attributes Block attributes.
|
||||
* @param string $content Block content.
|
||||
*
|
||||
* @return string Rendered block type output.
|
||||
*/
|
||||
protected function render( $attributes, $content ) {
|
||||
return $this->inject_html_data_attributes( $content . $this->get_markup(), $attributes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the markup for the Mini Cart block.
|
||||
*
|
||||
* @return string The HTML markup.
|
||||
*/
|
||||
protected function get_markup() {
|
||||
if ( is_admin() || WC()->is_rest_api_request() ) {
|
||||
// In the editor we will display the placeholder, so no need to load
|
||||
// real cart data and to print the markup.
|
||||
return '';
|
||||
}
|
||||
$cart_controller = new CartController();
|
||||
$cart = $cart_controller->get_cart_instance();
|
||||
$cart_contents_count = $cart->get_cart_contents_count();
|
||||
$cart_contents = $cart->get_cart();
|
||||
|
||||
// Force mobile styles.
|
||||
return '<div class="wc-block-mini-cart is-mobile">
|
||||
<button class="wc-block-mini-cart__button">' .
|
||||
sprintf(
|
||||
/* translators: %d is the number of products in the cart. */
|
||||
_n(
|
||||
'%d product',
|
||||
'%d products',
|
||||
$cart_contents_count,
|
||||
'woo-gutenberg-products-block'
|
||||
),
|
||||
$cart_contents_count
|
||||
) . '</button>
|
||||
<div class="wc-block-mini-cart__contents" hidden>' . $this->get_cart_contents_markup( $cart_contents ) . '</div>
|
||||
</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the markup of the Cart contents.
|
||||
*
|
||||
* @param array $cart_contents Array of contents in the cart.
|
||||
*
|
||||
* @return string The HTML markup.
|
||||
*/
|
||||
protected function get_cart_contents_markup( $cart_contents ) {
|
||||
return '<table class="wc-block-cart-items wc-block-mini-cart-items--is-loading" aria-hidden="true">
|
||||
<thead>
|
||||
<tr class="wc-block-cart-items__header">
|
||||
<th class="wc-block-cart-items__header-image"><span /></th>
|
||||
<th class="wc-block-cart-items__header-product"><span /></th>
|
||||
<th class="wc-block-cart-items__header-total"><span /></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>' . implode( array_map( array( $this, 'get_cart_item_markup' ), $cart_contents ) ) . '</tbody>
|
||||
</table>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the skeleton of a Cart item.
|
||||
*
|
||||
* @return string The skeleton HTML markup.
|
||||
*/
|
||||
protected function get_cart_item_markup() {
|
||||
return '<tr class="wc-block-cart-items__row">
|
||||
<td class="wc-block-cart-item__image">
|
||||
<div><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" width="1" height="1" /></div>
|
||||
</td>
|
||||
<td class="wc-block-cart-item__product">
|
||||
<div class="wc-block-cart-item__product-name"></div>
|
||||
<div class="wc-block-cart-item__individual-price"></div>
|
||||
<div class="wc-block-cart-item__product-metadata"></div>
|
||||
<div class="wc-block-components-quantity-selector">
|
||||
<input class="wc-block-components-quantity-selector__input" type="number" step="1" min="0" value="1" />
|
||||
<button class="wc-block-components-quantity-selector__button wc-block-components-quantity-selector__button--minus">-</button>
|
||||
<button class="wc-block-components-quantity-selector__button wc-block-components-quantity-selector__button--plus">+</button>
|
||||
</div>
|
||||
</td>
|
||||
<td class="wc-block-cart-item__total">
|
||||
<div class="wc-block-cart-item__price"></div>
|
||||
</td>
|
||||
</tr>';
|
||||
}
|
||||
}
|
|
@ -128,6 +128,7 @@ final class BlockTypesController {
|
|||
if ( Package::feature()->is_experimental_build() ) {
|
||||
$block_types[] = 'SingleProduct';
|
||||
$block_types[] = 'CheckoutI2';
|
||||
$block_types[] = 'MiniCart';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue