is_sold_individually() && $product->is_purchasable() && $product->is_in_stock(); } public function registerBlockModule() { if ( function_exists( 'wp_register_script_module' ) ) { wp_register_script_module( self::BLOCK_MODULE, ServiceContainer::getInstance()->getFileManager()->locateJSAsset( 'frontend/catalog-quantity' ), array( '@wordpress/interactivity' ), TierPricingTablePlugin::VERSION ); } } /** * The quantity box next to the Product Button block's button, on one line. The block keeps the * quantity it adds in its Interactivity API context; the box's module writes the typed quantity there. * * @param string $content * @param array $block * @param WP_Block|mixed $instance * * @return string */ public function renderInProductButtonBlock( $content, $block, $instance ) { if ( ! function_exists( 'wp_enqueue_script_module' ) || ! ( $instance instanceof WP_Block ) || empty( $instance->context['postId'] ) ) { return $content; } // only the button of a product list: inside the add-to-cart-with-options form the block has its own quantity selector if ( false === strpos( $content, 'actions.addCartItem' ) ) { return $content; } $product = wc_get_product( (int) $instance->context['postId'] ); if ( ! $this->wantsQuantityField( $product ) ) { return $content; } $quantity = woocommerce_quantity_input( array( 'min_value' => 1, 'max_value' => $product->backorders_allowed() ? '' : $product->get_stock_quantity(), ), $product, false ); if ( ! $quantity ) { return $content; } // the box is its own interactive region; the input feeds the button block's context $quantity = preg_replace( '/
]*\sstyle="([^"]*)"/', $content, $m ) && preg_match( '/margin-top:[^;"]+/', $m[1], $marginTop ) ) { $rowStyle = ' style="' . esc_attr( $marginTop[0] ) . ';"'; $content = preg_replace_callback( '/(]*\sstyle=")([^"]*)(")/', function ( $mm ) use ( $marginTop ) { return $mm[1] . trim( str_replace( $marginTop[0], '', $mm[2] ), '; ' ) . $mm[3]; }, $content, 1 ); } $wrapped = preg_replace_callback( '//s', function ( $mm ) use ( $quantity, $rowStyle ) { return '
' . $quantity . $mm[0] . '
'; }, $content, 1, $count ); if ( ! $count ) { return $content; } wp_enqueue_script_module( self::BLOCK_MODULE ); return $wrapped; } public function renderQuantityField() { // the product button block has no place for the field, and WooCommerce fires this classic hook there too if ( ProductCatalogLoop::isRenderingProductBlocks() ) { return; } $product = wc_get_product( get_the_ID() ); if ( $this->wantsQuantityField( $product ) ) { // the quantity box and the add-to-cart button share one line (a flex row, see main.css) if ( apply_filters( 'tiered_pricing_table/catalog/quantity_inline', true, $product ) ) { echo '
'; $this->wrapperOpen = true; } woocommerce_quantity_input( array( 'min_value' => 1, 'max_value' => $product->backorders_allowed() ? '' : $product->get_stock_quantity(), ) ); } } public function closeWrapper() { if ( $this->wrapperOpen ) { echo '
'; $this->wrapperOpen = false; } } public function enqueueQuantityHandlerScript() { $handle = 'tpt-catalog-quantity-input-handler'; wp_register_script( $handle, '', array( 'jquery' ), TierPricingTablePlugin::VERSION, true ); wp_enqueue_script( $handle ); $inlineJs = ' jQuery(".type-product").on("click", ".quantity input", function() { return false; }); jQuery(".type-product").on("change input", ".quantity .qty", function() { var add_to_cart_button = jQuery(this).closest(".product").find(".add_to_cart_button"); // For AJAX add-to-cart actions add_to_cart_button.attr("data-quantity", jQuery(this).val()); // For non-AJAX add-to-cart actions (the Product Button block is a