UNPKG

@mayurgodhani/ecomtools-cli

Version:

E-commerce tools MCP server for Shopify development

396 lines (351 loc) 10.3 kB
Title: Shopify Cart Drawer Description: An AJAX-powered sliding cart drawer for Shopify stores that updates in real-time. Code: <!-- Cart Drawer HTML --> <div id="cart-drawer" class="cart-drawer"> <div class="cart-drawer__header"> <h2>Your Cart</h2> <button class="cart-drawer__close" aria-label="Close cart"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </svg> </button> </div> <div class="cart-drawer__content"> <div class="cart-drawer__items"> {%- if cart.item_count > 0 -%} <form action="{{ routes.cart_url }}" method="post" id="cart-drawer-form"> {%- for item in cart.items -%} <div class="cart-drawer__item" data-item-key="{{ item.key }}"> <div class="cart-drawer__item-image"> <img src="{{ item.image | img_url: '120x' }}" alt="{{ item.title | escape }}"> </div> <div class="cart-drawer__item-details"> <a href="{{ item.url }}" class="cart-drawer__item-title"> {{ item.product.title }} </a> <div class="cart-drawer__item-variant"> {{ item.variant.title }} </div> <div class="cart-drawer__item-price"> {{ item.final_price | money }} </div> <div class="cart-drawer__item-quantity"> <button type="button" class="quantity-button" data-action="decrease">-</button> <input type="number" name="updates[{{ item.key }}]" value="{{ item.quantity }}" min="0" aria-label="Quantity"> <button type="button" class="quantity-button" data-action="increase">+</button> </div> <button type="button" class="cart-drawer__item-remove" data-item-key="{{ item.key }}"> Remove </button> </div> </div> {%- endfor -%} <div class="cart-drawer__footer"> <div class="cart-drawer__subtotal"> <span>Subtotal</span> <span>{{ cart.total_price | money }}</span> </div> <div class="cart-drawer__taxes"> {%- if cart.taxes_included -%} <small>Tax included. <a href="{{ shop.policies.shipping_policy.url }}">Shipping</a> calculated at checkout.</small> {%- else -%} <small>Tax and <a href="{{ shop.policies.shipping_policy.url }}">shipping</a> calculated at checkout.</small> {%- endif -%} </div> <button type="submit" name="checkout" class="cart-drawer__checkout-button"> Checkout </button> <a href="{{ routes.cart_url }}" class="cart-drawer__view-cart"> View Cart </a> </div> </form> {%- else -%} <div class="cart-drawer__empty"> <p>Your cart is currently empty.</p> <a href="{{ routes.all_products_collection_url }}" class="cart-drawer__continue-shopping"> Continue Shopping </a> </div> {%- endif -%} </div> </div> </div> <!-- Cart Drawer JavaScript --> <script> document.addEventListener('DOMContentLoaded', function() { const cartDrawer = document.getElementById('cart-drawer'); const cartToggle = document.querySelectorAll('.cart-toggle'); const cartClose = document.querySelector('.cart-drawer__close'); const cartOverlay = document.getElementById('cart-overlay'); // Open cart drawer cartToggle.forEach(function(toggle) { toggle.addEventListener('click', function(e) { e.preventDefault(); openCartDrawer(); }); }); // Close cart drawer if (cartClose) { cartClose.addEventListener('click', closeCartDrawer); } if (cartOverlay) { cartOverlay.addEventListener('click', closeCartDrawer); } // Close cart drawer when pressing ESC key document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeCartDrawer(); } }); // Handle quantity buttons const quantityButtons = document.querySelectorAll('.quantity-button'); quantityButtons.forEach(function(button) { button.addEventListener('click', function() { const input = this.parentNode.querySelector('input'); const currentValue = parseInt(input.value); if (this.dataset.action === 'increase') { input.value = currentValue + 1; } else if (this.dataset.action === 'decrease' && currentValue > 1) { input.value = currentValue - 1; } updateCart(input.name.replace('updates[', '').replace(']', ''), input.value); }); }); // Handle remove buttons const removeButtons = document.querySelectorAll('.cart-drawer__item-remove'); removeButtons.forEach(function(button) { button.addEventListener('click', function() { const itemKey = this.dataset.itemKey; updateCart(itemKey, 0); }); }); // Update cart via AJAX function updateCart(itemKey, quantity) { fetch('/cart/change.js', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: itemKey, quantity: quantity }) }) .then(response => response.json()) .then(cart => { refreshCart(); }) .catch(error => { console.error('Error updating cart:', error); }); } // Refresh cart contents function refreshCart() { fetch('/cart?view=drawer') .then(response => response.text()) .then(html => { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const newContent = doc.querySelector('.cart-drawer__content'); if (newContent) { document.querySelector('.cart-drawer__content').innerHTML = newContent.innerHTML; } // Re-attach event listeners attachEventListeners(); }) .catch(error => { console.error('Error refreshing cart:', error); }); } function openCartDrawer() { cartDrawer.classList.add('cart-drawer--open'); document.body.classList.add('cart-drawer-open'); if (cartOverlay) { cartOverlay.classList.add('cart-overlay--active'); } } function closeCartDrawer() { cartDrawer.classList.remove('cart-drawer--open'); document.body.classList.remove('cart-drawer-open'); if (cartOverlay) { cartOverlay.classList.remove('cart-overlay--active'); } } function attachEventListeners() { // Re-attach event listeners after cart refresh // Repeat the event listener attachment code for quantity buttons and remove buttons } }); </script> <!-- Cart Drawer CSS --> <style> .cart-drawer { position: fixed; top: 0; right: 0; width: 400px; max-width: 90vw; height: 100vh; background-color: #fff; box-shadow: -2px 0 10px rgba(0,0,0,0.1); z-index: 1000; transform: translateX(100%); transition: transform 0.3s ease-in-out; display: flex; flex-direction: column; } .cart-drawer--open { transform: translateX(0); } .cart-drawer__header { padding: 1rem; border-bottom: 1px solid #e5e5e5; display: flex; justify-content: space-between; align-items: center; } .cart-drawer__close { background: none; border: none; cursor: pointer; padding: 0.5rem; } .cart-drawer__content { flex: 1; overflow-y: auto; padding: 1rem; } .cart-drawer__item { display: flex; margin-bottom: 1.5rem; padding-bottom: 1.5rem; border-bottom: 1px solid #e5e5e5; } .cart-drawer__item-image { width: 80px; margin-right: 1rem; } .cart-drawer__item-details { flex: 1; } .cart-drawer__item-title { font-weight: 500; margin-bottom: 0.25rem; display: block; text-decoration: none; color: #333; } .cart-drawer__item-variant { font-size: 0.875rem; color: #666; margin-bottom: 0.5rem; } .cart-drawer__item-price { font-weight: 500; margin-bottom: 0.5rem; } .cart-drawer__item-quantity { display: flex; align-items: center; margin-bottom: 0.5rem; } .cart-drawer__item-quantity input { width: 40px; text-align: center; margin: 0 0.5rem; border: 1px solid #e5e5e5; padding: 0.25rem; } .quantity-button { background: none; border: 1px solid #e5e5e5; width: 25px; height: 25px; display: flex; align-items: center; justify-content: center; cursor: pointer; } .cart-drawer__item-remove { background: none; border: none; color: #666; text-decoration: underline; cursor: pointer; padding: 0; font-size: 0.875rem; } .cart-drawer__footer { margin-top: 2rem; } .cart-drawer__subtotal { display: flex; justify-content: space-between; font-weight: 500; margin-bottom: 1rem; } .cart-drawer__taxes { margin-bottom: 1.5rem; font-size: 0.875rem; color: #666; } .cart-drawer__checkout-button { width: 100%; padding: 0.75rem 1rem; background-color: #000; color: #fff; border: none; cursor: pointer; font-weight: 500; margin-bottom: 0.75rem; } .cart-drawer__view-cart { display: block; text-align: center; color: #333; text-decoration: none; } .cart-drawer__empty { text-align: center; padding: 2rem 0; } .cart-drawer__continue-shopping { display: inline-block; margin-top: 1rem; padding: 0.5rem 1rem; background-color: #000; color: #fff; text-decoration: none; } .cart-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 999; opacity: 0; visibility: hidden; transition: opacity 0.3s; } .cart-overlay--active { opacity: 1; visibility: visible; } @media (max-width: 480px) { .cart-drawer { width: 100%; max-width: 100%; } } </style> <!-- Don't forget to add this overlay element to your theme.liquid --> <div id="cart-overlay" class="cart-overlay"></div> <!-- Add this button somewhere in your navigation --> <button class="cart-toggle"> Cart <span class="cart-count">{{ cart.item_count }}</span> </button>