feat: initial release of CommerceKit Floating Cart add-on v1.0.0

This commit is contained in:
2026-03-05 08:01:33 +01:00
commit dbf48a692d
3 changed files with 454 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
/* ==========================================================================
CommerceKit Floating Cart
========================================================================== */
/* Container fixed to bottom-right, above most theme layers */
#cgkit-floating-cart {
position: fixed;
bottom: 28px;
right: 28px;
z-index: 99998; /* below most modals/overlays but above normal content */
}
/* The circle button */
.cgkit-floating-cart__btn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 56px;
height: 56px;
padding: 0;
border: none;
border-radius: 50%;
background-color: #2c2c2c;
color: #ffffff;
cursor: pointer;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.22);
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
/* Prevent tap-highlight on mobile */
-webkit-tap-highlight-color: transparent;
}
.cgkit-floating-cart__btn:hover,
.cgkit-floating-cart__btn:focus-visible {
background-color: #444444;
transform: scale(1.08);
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.3);
outline: none;
}
.cgkit-floating-cart__btn:active {
transform: scale(0.96);
}
/* Cart icon SVG */
.cgkit-floating-cart__icon {
display: block;
width: 22px;
height: 22px;
pointer-events: none;
}
/* Item-count badge */
.cgkit-floating-cart__count {
position: absolute;
top: -4px;
right: -4px;
min-width: 20px;
height: 20px;
padding: 0 4px;
border-radius: 10px;
background-color: #e84040;
color: #ffffff;
font-size: 11px;
font-weight: 700;
line-height: 20px;
text-align: center;
pointer-events: none;
transition: transform 0.15s ease;
}
/* Hide badge when cart is empty */
.cgkit-floating-cart__count--hidden {
display: none;
}
/* "Bump" animation when count changes */
.cgkit-floating-cart__count--bump {
animation: cgkit-fc-bump 0.3s ease;
}
@keyframes cgkit-fc-bump {
0% { transform: scale(1); }
50% { transform: scale(1.4); }
100% { transform: scale(1); }
}
/* "Pulse" ring that briefly appears after add-to-cart */
.cgkit-floating-cart__btn::after {
content: '';
position: absolute;
inset: 0;
border-radius: 50%;
border: 2px solid #2c2c2c;
opacity: 0;
transform: scale(1);
}
.cgkit-floating-cart__btn.cgkit-fc--pulse::after {
animation: cgkit-fc-pulse 0.5s ease-out forwards;
}
@keyframes cgkit-fc-pulse {
0% { opacity: 0.6; transform: scale(1); }
100% { opacity: 0; transform: scale(1.6); }
}
/* Responsive: shrink slightly on small screens */
@media (max-width: 480px) {
#cgkit-floating-cart {
bottom: 18px;
right: 18px;
}
.cgkit-floating-cart__btn {
width: 50px;
height: 50px;
}
.cgkit-floating-cart__icon {
width: 20px;
height: 20px;
}
}

147
assets/js/floating-cart.js Normal file
View File

@@ -0,0 +1,147 @@
/**
* CommerceKit Floating Cart frontend script
*
* Responsibilities:
* 1. Open the CommerceKit / Shoptimizer minicart when the floating button is clicked.
* 2. Automatically open the minicart after a successful add-to-cart (AJAX or standard).
* 3. Play a small badge-bump animation when the item count changes.
*
* All selectors and feature flags come from the `cgkitFC` object localised by PHP.
*/
( function ( $ ) {
'use strict';
/* -----------------------------------------------------------------------
* Module
* --------------------------------------------------------------------- */
var FloatingCart = {
$btn: null,
$count: null,
prevCount: 0,
/** Bootstrap */
init: function () {
if ( typeof cgkitFC === 'undefined' ) {
return;
}
this.$btn = $( '#cgkit-floating-cart .cgkit-floating-cart__btn' );
this.$count = $( '#cgkit-fc-count' );
if ( ! this.$btn.length ) {
return;
}
this.prevCount = parseInt( this.$count.text(), 10 ) || 0;
this.bindEvents();
},
/** Attach event listeners */
bindEvents: function () {
var self = this;
// --- Floating button click → open minicart -----------------------
this.$btn.on( 'click', function ( e ) {
e.preventDefault();
self.openMinicart();
} );
// --- Auto-open after AJAX add-to-cart ----------------------------
if ( cgkitFC.autoOpen === 'yes' ) {
$( document.body ).on( 'added_to_cart', function () {
setTimeout( function () {
self.openMinicart();
self.pulseBtn();
}, parseInt( cgkitFC.autoOpenDelay, 10 ) || 400 );
} );
}
// --- Badge bump when WooCommerce refreshes fragments -------------
$( document.body ).on(
'wc_fragments_refreshed wc_fragments_loaded',
function () {
self.maybeBumpBadge();
}
);
},
/**
* Trigger the theme's minicart open mechanism.
*
* Strategy:
* 1. Try clicking the first element matching the configured selector.
* 2. If that element has no jQuery click handlers registered, dispatch
* a native 'click' so themes that use addEventListener also fire.
* 3. Last resort: navigate to the cart URL.
*/
openMinicart: function () {
var selectors = ( cgkitFC.minicartTrigger || '' ).split( ',' );
var $trigger = null;
for ( var i = 0; i < selectors.length; i++ ) {
var $el = $( $.trim( selectors[ i ] ) ).first();
if ( $el.length ) {
$trigger = $el;
break;
}
}
if ( $trigger && $trigger.length ) {
// Fire both jQuery and native click so all listeners catch it.
$trigger.trigger( 'click' );
var nativeEl = $trigger.get( 0 );
if ( nativeEl && typeof nativeEl.click === 'function' ) {
nativeEl.click();
}
return;
}
// No trigger found → fall back to cart page
if ( cgkitFC.cartUrl ) {
window.location.href = cgkitFC.cartUrl;
}
},
/**
* Add a pulse ring on the button after add-to-cart.
*/
pulseBtn: function () {
var self = this;
this.$btn.addClass( 'cgkit-fc--pulse' );
setTimeout( function () {
self.$btn.removeClass( 'cgkit-fc--pulse' );
}, 600 );
},
/**
* If the item count increased, animate the badge.
*/
maybeBumpBadge: function () {
var $freshCount = $( '#cgkit-fc-count' );
// Re-query in case the fragment replaced the element.
if ( ! $freshCount.length ) {
return;
}
this.$count = $freshCount;
var newCount = parseInt( this.$count.text(), 10 ) || 0;
if ( newCount !== this.prevCount ) {
this.prevCount = newCount;
this.$count.removeClass( 'cgkit-floating-cart__count--bump' );
// Force reflow so removing + re-adding the class restarts the animation.
void this.$count[ 0 ].offsetWidth; // eslint-disable-line no-unused-expressions
this.$count.addClass( 'cgkit-floating-cart__count--bump' );
}
}
};
/* -----------------------------------------------------------------------
* Boot
* --------------------------------------------------------------------- */
$( document ).ready( function () {
FloatingCart.init();
} );
} )( jQuery );