'after', 'anchor' => 'core/navigation', 'area' => 'header', 'version' => '8.4.0', ), ); /** * WooCommerce mini-cart template blocks. * * @var array */ const MINI_CART_TEMPLATE_BLOCKS = array( 'woocommerce/mini-cart-contents', 'woocommerce/filled-mini-cart-contents-block', 'woocommerce/mini-cart-title-block', 'woocommerce/mini-cart-title-label-block', 'woocommerce/mini-cart-title-items-counter-block', 'woocommerce/mini-cart-items-block', 'woocommerce/mini-cart-products-table-block', 'woocommerce/mini-cart-footer-block', 'woocommerce/mini-cart-cart-button-block', 'woocommerce/mini-cart-checkout-button-block', 'woocommerce/empty-mini-cart-contents-block', 'woocommerce/mini-cart-shopping-button-block', ); /** * Constructor. * * @param AssetApi $asset_api Instance of the asset API. * @param AssetDataRegistry $asset_data_registry Instance of the asset data registry. * @param IntegrationRegistry $integration_registry Instance of the integration registry. */ public function __construct( AssetApi $asset_api, AssetDataRegistry $asset_data_registry, IntegrationRegistry $integration_registry ) { parent::__construct( $asset_api, $asset_data_registry, $integration_registry, $this->block_name ); } /** * Initialize this block type. * * - Hook into WP lifecycle. * - Register the block with WordPress. */ protected function initialize() { parent::initialize(); add_action( 'wp_loaded', array( $this, 'register_empty_cart_message_block_pattern' ) ); add_filter( 'hooked_block_woocommerce/mini-cart', array( $this, 'modify_hooked_block_attributes' ), 10, 5 ); add_filter( 'hooked_block_types', array( $this, 'register_hooked_block' ), 9, 4 ); } /** * Callback for the Block Hooks API to modify the attributes of the hooked block. * * @param array|null $parsed_hooked_block The parsed block array for the given hooked block type, or null to suppress the block. * @param string $hooked_block_type The hooked block type name. * @param string $relative_position The relative position of the hooked block. * @param array $parsed_anchor_block The anchor block, in parsed block array format. * @param WP_Block_Template|WP_Post|array $context The block template, template part, `wp_navigation` post type, * or pattern that the anchor block belongs to. * @return array|null */ public function modify_hooked_block_attributes( $parsed_hooked_block, $hooked_block_type, $relative_position, $parsed_anchor_block, $context ) { $mini_cart_block_font_size = wp_get_global_styles( array( 'blocks', 'woocommerce/mini-cart', 'typography', 'fontSize' ) ); if ( ! is_string( $mini_cart_block_font_size ) ) { $navigation_block_font_size = wp_get_global_styles( array( 'blocks', 'core/navigation', 'typography', 'fontSize' ) ); if ( is_string( $navigation_block_font_size ) ) { $parsed_hooked_block['attrs']['style']['typography']['fontSize'] = $navigation_block_font_size; } } return $parsed_hooked_block; } /** * 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 null */ protected function get_block_type_script( $key = null ) { return null; } /** * 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 = array() ) { if ( is_cart() || is_checkout() ) { return; } parent::enqueue_data( $attributes ); // Hydrate the following data depending on admin or frontend context. if ( ! is_admin() && ! WC()->is_rest_api_request() ) { $label_info = $this->get_tax_label(); $this->tax_label = $label_info['tax_label']; $this->display_cart_prices_including_tax = $label_info['display_cart_prices_including_tax']; $this->asset_data_registry->add( 'taxLabel', $this->tax_label ); } $this->asset_data_registry->add( 'displayCartPricesIncludingTax', $this->display_cart_prices_including_tax ); $template_part_edit_uri = ''; if ( current_user_can( 'edit_theme_options' ) && ( wp_is_block_theme() || current_theme_supports( 'block-template-parts' ) ) ) { $theme_slug = BlockTemplateUtils::theme_has_template_part( 'mini-cart' ) ? wp_get_theme()->get_stylesheet() : BlockTemplateUtils::PLUGIN_SLUG; if ( version_compare( get_bloginfo( 'version' ), '5.9', '<' ) ) { $site_editor_uri = add_query_arg( array( 'page' => 'gutenberg-edit-site' ), admin_url( 'themes.php' ) ); } else { $site_editor_uri = add_query_arg( array( 'canvas' => 'edit', 'path' => '/template-parts/single', ), admin_url( 'site-editor.php' ) ); } $template_part_edit_uri = esc_url_raw( add_query_arg( array( 'postId' => sprintf( '%s//%s', $theme_slug, 'mini-cart' ), 'postType' => 'wp_template_part', ), $site_editor_uri ) ); } $this->asset_data_registry->add( 'templatePartEditUri', $template_part_edit_uri ); /** * Fires after cart block data is registered. * * @since 5.8.0 */ do_action( 'woocommerce_blocks_cart_enqueue_data' ); } /** * Returns the markup for the cart price. * * @param array $attributes Block attributes. * * @return string */ protected function get_cart_price_markup( $attributes ) { if ( isset( $attributes['hasHiddenPrice'] ) && false !== $attributes['hasHiddenPrice'] ) { return; } $price_color = isset( $attributes['priceColor']['color'] ) ? $attributes['priceColor']['color'] : ''; return '' . $this->get_include_tax_label_markup( $attributes ); } /** * Returns the markup for render the tax label. * * @param array $attributes Block attributes. * * @return string */ protected function get_include_tax_label_markup( $attributes ) { if ( empty( $this->tax_label ) ) { return ''; } $price_color = isset( $attributes['priceColor']['color'] ) ? $attributes['priceColor']['color'] : ''; return '' . esc_html( $this->tax_label ) . ''; } /** * Render the Mini-Cart block. * * @param array $attributes Block attributes. * @param string $content Block content. * @param \WP_Block $block Block instance. * @return string Rendered block type output. */ protected function render( $attributes, $content, $block ) { /** * Do not render for logged-out users if the Coming Soon mode is enabled for store pages only. */ $coming_soon_helper = wc_get_container()->get( ComingSoonHelper::class ); if ( ! is_user_logged_in() && ! WC()->is_rest_api_request() && $coming_soon_helper->is_store_coming_soon() ) { return ''; } /** * In the cart and checkout pages, the block is either rendered hidden or removed. * It is not interactive, so it can fall back to the existing implementation. */ if ( is_cart() || is_checkout() ) { return $content . $this->get_markup( MiniCartUtils::migrate_attributes_to_color_panel( $attributes ) ); } wp_enqueue_script_module( $this->get_full_block_name() ); // Enqueue all integration scripts registered for this block. $integration_script_handles = $this->integration_registry->get_all_registered_script_handles(); foreach ( $integration_script_handles as $handle ) { wp_enqueue_script( $handle ); } $consent = 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WooCommerce'; BlocksSharedState::load_cart_state( $consent ); BlocksSharedState::load_store_config( $consent ); BlocksSharedState::load_placeholder_image( $consent ); $cart = $this->get_cart_instance(); if ( $cart ) { $classes_styles = StyleAttributesUtils::get_classes_and_styles_by_attributes( $attributes ); $icon_color = isset( $attributes['iconColor']['color'] ) ? esc_attr( $attributes['iconColor']['color'] ) : 'currentColor'; $product_count_color = isset( $attributes['productCountColor']['color'] ) ? $attributes['productCountColor']['color'] : ''; $styles = $product_count_color ? 'background:' . esc_attr( $product_count_color ) : ''; $icon = MiniCartUtils::get_svg_icon( $attributes['miniCartIcon'] ?? '', $icon_color ); $product_count_visibility = isset( $attributes['productCountVisibility'] ) ? $attributes['productCountVisibility'] : 'greater_than_zero'; $wrapper_classes = sprintf( 'wc-block-mini-cart wp-block-woocommerce-mini-cart %s', $classes_styles['classes'] ); $wrapper_styles = $classes_styles['styles']; // Pre-render the template part so nested blocks enqueue their assets before the overlay is printed in wp_footer. $template_part_contents = $this->get_template_part_contents( false ); $template_part_contents = $this->render_template_part_contents( $template_part_contents ); $cart_item_count = $cart ? $cart->get_cart_contents_count() : 0; $display_cart_price_including_tax = get_option( 'woocommerce_tax_display_cart' ) === TaxDisplayMode::INCLUSIVE; $cart_item_count = $cart ? $cart->get_cart_contents_count() : 0; $badge_is_visible = ( 'always' === $product_count_visibility ) || ( 'never' !== $product_count_visibility && $cart_item_count > 0 ); $formatted_subtotal = ''; $html = new \WP_HTML_Tag_Processor( wc_price( $cart->get_displayed_subtotal() ) ); $on_cart_click_behaviour = isset( $attributes['onCartClickBehaviour'] ) ? $attributes['onCartClickBehaviour'] : 'open_drawer'; if ( $html->next_tag( 'bdi' ) ) { while ( $html->next_token() ) { if ( '#text' === $html->get_token_name() ) { $formatted_subtotal .= $html->get_modifiable_text(); } } } // The following translation is a temporary workaround. It will be // reverted to the previous form (`%1$d item in cart`) as soon as the // `@wordpress/i18n` package is available as a script module. $button_aria_label_template = isset( $attributes['hasHiddenPrice'] ) && false !== $attributes['hasHiddenPrice'] /* translators: %d is the number of products in the cart. */ ? __( 'Number of items in the cart: %d', 'woocommerce' ) /* translators: %1$d is the number of products in the cart. %2$s is the cart total */ : __( 'Number of items in the cart: %1$d. Total price of %2$s', 'woocommerce' ); wp_interactivity_state( $this->get_full_block_name(), array( 'isOpen' => false, 'totalItemsInCart' => $cart_item_count, 'shouldShowTaxLabel' => $cart->get_cart_contents_tax() > 0, 'badgeIsVisible' => $badge_is_visible, 'formattedSubtotal' => $formatted_subtotal, 'drawerOverlayClass' => 'wc-block-components-drawer__screen-overlay wc-block-components-drawer__screen-overlay--with-slide-out wc-block-components-drawer__screen-overlay--is-hidden', 'buttonAriaLabel' => function () use ( $button_aria_label_template ) { $state = wp_interactivity_state(); return isset( $attributes['hasHiddenPrice'] ) && false !== $attributes['hasHiddenPrice'] ? sprintf( $button_aria_label_template, $state['totalItemsInCart'] ) : sprintf( $button_aria_label_template, $state['totalItemsInCart'], $state['formattedSubtotal'] ); }, 'productCountColor' => $product_count_color, ) ); $context = array( 'productCountVisibility' => $product_count_visibility, ); wp_interactivity_config( $this->get_full_block_name(), array( 'displayCartPriceIncludingTax' => $display_cart_price_including_tax, 'onCartClickBehaviour' => $on_cart_click_behaviour, 'checkoutUrl' => wc_get_checkout_url(), 'buttonAriaLabelTemplate' => $button_aria_label_template, ) ); $cart_always_shows_price = isset( $attributes['hasHiddenPrice'] ) && false === $attributes['hasHiddenPrice']; $price_color = isset( $attributes['priceColor']['color'] ) ? $attributes['priceColor']['color'] : ''; $button_role = 'navigate_to_checkout' === $on_cart_click_behaviour ? 'role="link"' : ''; // Render the minicart overlay in the body, outside of the block itself. if ( ! has_action( 'wp_footer', array( $this, 'render_mini_cart_overlay' ) ) ) { add_action( 'wp_footer', array( $this, 'render_mini_cart_overlay' ) ); } ob_start(); ?>
' . __( 'Your shopping cart is empty', 'woocommerce' ) . '
', ) ); } /** * Returns whether the Mini-Cart should be rendered or not. * * @param array $attributes Block attributes. * * @return bool */ public function should_not_render_mini_cart( array $attributes ) { return isset( $attributes['cartAndCheckoutRenderStyle'] ) && 'hidden' !== $attributes['cartAndCheckoutRenderStyle']; } }