208 lines
6.7 KiB
PHP
208 lines
6.7 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: CommerceKit Floating Cart
|
|
* Plugin URI: https://www.commercegurus.com
|
|
* Description: Adds a floating cart icon (bottom-right) and auto-opens the CommerceKit minicart after add to cart. Requires CommerceGurus CommerceKit and WooCommerce.
|
|
* Version: 1.0.2
|
|
* Author: CommerceGurus
|
|
* Author URI: https://www.commercegurus.com
|
|
* License: GPLv3
|
|
* License URI: http://www.gnu.org/licenses/gpl.html
|
|
* Text Domain: cgkit-floating-cart
|
|
* Requires at least: 5.6
|
|
* Requires PHP: 7.4
|
|
* WC requires at least: 5.0
|
|
* WC tested up to: 9.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
define( 'CGKIT_FC_VERSION', '1.0.2' );
|
|
define( 'CGKIT_FC_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
|
define( 'CGKIT_FC_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
|
|
|
/**
|
|
* Declare WooCommerce HPOS compatibility.
|
|
*/
|
|
add_action( 'before_woocommerce_init', function () {
|
|
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
|
|
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
|
|
}
|
|
} );
|
|
|
|
/**
|
|
* Main plugin class.
|
|
*/
|
|
class CommerceKit_Floating_Cart {
|
|
|
|
private static $instance = null;
|
|
|
|
public static function get_instance(): self {
|
|
if ( null === self::$instance ) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
private function __construct() {
|
|
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
|
add_action( 'wp_footer', [ $this, 'render_floating_cart' ] );
|
|
add_filter( 'woocommerce_add_to_cart_fragments', [ $this, 'cart_count_fragment' ] );
|
|
|
|
// Disable WooCommerce's "redirect to cart page after add" so the page
|
|
// stays put and we can open the minicart instead.
|
|
add_filter( 'pre_option_woocommerce_cart_redirect_after_add', [ $this, 'disable_cart_redirect' ] );
|
|
|
|
// Ensure WooCommerce's AJAX add-to-cart is active so the body receives
|
|
// the `added_to_cart` JS event that our script listens for.
|
|
add_filter( 'pre_option_woocommerce_enable_ajax_add_to_cart', [ $this, 'enable_ajax_add_to_cart' ] );
|
|
}
|
|
|
|
/**
|
|
* Return 'no' so WooCommerce never redirects to /cart after adding a product.
|
|
*/
|
|
public function disable_cart_redirect( string $value ): string {
|
|
return 'no';
|
|
}
|
|
|
|
/**
|
|
* Return 'yes' so WooCommerce uses AJAX add-to-cart on archive/shop pages.
|
|
* This ensures `added_to_cart` JS event fires without a page reload.
|
|
*/
|
|
public function enable_ajax_add_to_cart( string $value ): string {
|
|
// Only override when it's not already enabled.
|
|
return ( 'yes' === $value ) ? $value : 'yes';
|
|
}
|
|
|
|
/**
|
|
* Enqueue frontend CSS and JS.
|
|
*/
|
|
public function enqueue_assets(): void {
|
|
wp_enqueue_style(
|
|
'cgkit-floating-cart',
|
|
CGKIT_FC_PLUGIN_URL . 'assets/css/floating-cart.css',
|
|
[],
|
|
CGKIT_FC_VERSION
|
|
);
|
|
|
|
wp_enqueue_script(
|
|
'cgkit-floating-cart',
|
|
CGKIT_FC_PLUGIN_URL . 'assets/js/floating-cart.js',
|
|
[ 'jquery' ],
|
|
CGKIT_FC_VERSION,
|
|
true
|
|
);
|
|
|
|
/**
|
|
* Filter: cgkit_fc_minicart_trigger
|
|
*
|
|
* CSS selector(s) for the theme's minicart open button. The floating
|
|
* cart icon and the add-to-cart auto-open both programmatically click
|
|
* the first matching element.
|
|
*
|
|
* For Shoptimizer / CommerceKit the default covers the most common
|
|
* header cart selectors. Override this filter in your child theme if
|
|
* needed.
|
|
*
|
|
* @param string $selector A comma-separated CSS selector string.
|
|
*/
|
|
$trigger = apply_filters(
|
|
'cgkit_fc_minicart_trigger',
|
|
'.shoptimizer-cart .cart-contents, .site-header-cart .cart-contents, .wcmenucart, .header-cart-link'
|
|
);
|
|
|
|
/**
|
|
* Filter: cgkit_fc_auto_open
|
|
*
|
|
* Set to false to disable auto-opening the minicart after add to cart.
|
|
*
|
|
* @param bool $auto_open
|
|
*/
|
|
$auto_open = (bool) apply_filters( 'cgkit_fc_auto_open', true );
|
|
|
|
/**
|
|
* Filter: cgkit_fc_auto_open_delay
|
|
*
|
|
* Milliseconds to wait before opening the minicart after add to cart,
|
|
* to allow WooCommerce fragments to refresh first.
|
|
*
|
|
* @param int $delay
|
|
*/
|
|
$delay = (int) apply_filters( 'cgkit_fc_auto_open_delay', 400 );
|
|
|
|
wp_localize_script( 'cgkit-floating-cart', 'cgkitFC', [
|
|
'minicartTrigger' => $trigger,
|
|
'autoOpen' => $auto_open ? 'yes' : 'no',
|
|
'autoOpenDelay' => $delay,
|
|
'cartUrl' => wc_get_cart_url(),
|
|
] );
|
|
}
|
|
|
|
/**
|
|
* Output the floating cart button HTML in the footer.
|
|
*/
|
|
public function render_floating_cart(): void {
|
|
$count = WC()->cart ? WC()->cart->get_cart_contents_count() : 0;
|
|
?>
|
|
<div id="cgkit-floating-cart" class="cgkit-floating-cart" role="complementary" aria-label="<?php esc_attr_e( 'Floating cart', 'cgkit-floating-cart' ); ?>">
|
|
<button class="cgkit-floating-cart__btn" type="button" aria-label="<?php esc_attr_e( 'Open mini cart', 'cgkit-floating-cart' ); ?>">
|
|
<?php echo $this->cart_icon_svg(); // phpcs:ignore WordPress.Security.EscapeOutput ?>
|
|
<?php echo $this->count_badge_html( $count ); // phpcs:ignore WordPress.Security.EscapeOutput ?>
|
|
</button>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Provide a WooCommerce fragment so the badge count updates via AJAX
|
|
* (e.g. after mini-cart quantity changes or AJAX add-to-cart).
|
|
*
|
|
* @param array $fragments
|
|
* @return array
|
|
*/
|
|
public function cart_count_fragment( array $fragments ): array {
|
|
$count = WC()->cart ? WC()->cart->get_cart_contents_count() : 0;
|
|
$fragments['#cgkit-fc-count'] = $this->count_badge_html( $count );
|
|
return $fragments;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// Private helpers
|
|
// -------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Return the count badge HTML (targeted by fragments).
|
|
*/
|
|
private function count_badge_html( int $count ): string {
|
|
$hidden = 0 === $count ? ' cgkit-floating-cart__count--hidden' : '';
|
|
return sprintf(
|
|
'<span id="cgkit-fc-count" class="cgkit-floating-cart__count%s" aria-hidden="true">%s</span>',
|
|
esc_attr( $hidden ),
|
|
esc_html( $count > 0 ? $count : '' )
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Shopping-cart SVG icon (Feather icons style, matches CommerceKit aesthetic).
|
|
*/
|
|
private function cart_icon_svg(): string {
|
|
return '<svg class="cgkit-floating-cart__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
|
width="24" height="24" aria-hidden="true" focusable="false"
|
|
fill="none" stroke="currentColor" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round">
|
|
<circle cx="9" cy="21" r="1"></circle>
|
|
<circle cx="20" cy="21" r="1"></circle>
|
|
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
|
|
</svg>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bootstrap after WooCommerce is loaded so WC() is available.
|
|
*/
|
|
add_action( 'woocommerce_loaded', function () {
|
|
CommerceKit_Floating_Cart::get_instance();
|
|
} );
|