1import { events } from '@dropins/tools/event-bus.js'; 2import { InLineAlert, Icon, provider as UI } from '@dropins/tools/components.js'; 3import { h, Fragment } from '@dropins/tools/preact.js'; 4import { initializeDropin } from './index.js'; 5import { fetchPlaceholders, rootLink } from '../commerce.js'; 6import { loadCSS } from '../aem.js'; 7import { insertAlertAfterBreadcrumbs } from '../utils/insert-alert-after-breadcrumbs.js'; 8 9await initializeDropin(async () => { 10 // Load cart alert styles 11 await loadCSS(`${window.hlx.codeBasePath}/styles/cart-alert.css`); 12 13 const placeholders = await fetchPlaceholders(); 14 const main = document.querySelector('main'); 15 if (!main) return; 16 17 let currentAlert = null; 18 // Track whether alert should be shown for the next cart operation 19 let shouldShowAlert = false; 20 21 /** 22 * Builds heading: links the phrase "shopping cart" to the cart URL (placeholder or default). 23 * @param {string} productName 24 * @param {string} [placeholderTemplate] 25 * @param {string} cartURL 26 */ 27 const buildCartAddedHeading = (productName, placeholderTemplate, cartURL) => { 28 const text = placeholderTemplate 29 ? placeholderTemplate.replace('{product}', productName) 30 : `You added ${productName} to your shopping cart.`; 31 32 const match = text.match(/shopping cart/i); 33 if (!match || match.index === undefined) { 34 return text; 35 } 36 37 const start = match.index; 38 const end = start + match[0].length; 39 const linkLabel = text.slice(start, end); 40 41 return h( 42 Fragment, 43 null, 44 text.slice(0, start), 45 h('a', { 46 href: cartURL, 47 className: 'cart-alert__link', 48 }, linkLabel), 49 text.slice(end), 50 ); 51 }; 52 53 /** 54 * Creates and displays a cart success alert 55 * @param {Array} items - Array of cart items that were added or updated 56 */ 57 const showCartAlert = async (items) => { 58 // Only show alert if explicitly requested 59 if (!shouldShowAlert) { 60 return; 61 } 62 63 // Reset flag after checking 64 shouldShowAlert = false; 65 if (!items || items.length === 0) return; 66 67 // Get the first item's product name 68 const productName = items[0]?.name || items[0]?.product?.name || 'Product'; 69 const cartURL = rootLink('/cart'); 70 71 // Remove any existing alert 72 const existingAlert = main.querySelector('.cart-alert-container'); 73 if (existingAlert) { 74 existingAlert.remove(); 75 currentAlert = null; 76 } 77 78 // Create alert container after breadcrumbs (or first in main as fallback) 79 const alertContainer = document.createElement('div'); 80 alertContainer.className = 'cart-alert-container'; 81 alertContainer.setAttribute('role', 'region'); 82 alertContainer.setAttribute('aria-live', 'polite'); 83 84 insertAlertAfterBreadcrumbs(main, alertContainer); 85 86 const heading = buildCartAddedHeading( 87 productName, 88 placeholders?.Global?.CartAddedMessage, 89 cartURL, 90 ); 91 92 currentAlert = await UI.render(InLineAlert, { 93 heading, 94 type: 'success', 95 variant: 'primary', 96 icon: h(Icon, { source: 'CheckWithCircle' }), 97 'aria-live': 'assertive', 98 role: 'alert', 99 onDismiss: () => { 100 if (currentAlert && alertContainer.parentNode) { 101 alertContainer.remove(); 102 currentAlert = null; 103 } 104 }, 105 })(alertContainer); 106 107 // Scroll so the breadcrumb alert is visible (same pattern as wishlist-alert). 108 setTimeout(() => { 109 window.scrollTo({ top: 0, behavior: 'smooth' }); 110 }, 100); 111 112 // Auto-dismiss after 5 seconds 113 setTimeout(() => { 114 if (currentAlert && alertContainer.parentNode) { 115 alertContainer.remove(); 116 currentAlert = null; 117 } 118 }, 5000); 119 }; 120 121 // Listen for custom event that indicates whether alert should be shown 122 // This event should be emitted BEFORE calling cart API 123 // (addProductsToCart or updateProductsFromCart) 124 events.on('cart/action/before', (metadata) => { 125 // Set flag based on showAlert property (defaults to false if not specified) 126 shouldShowAlert = metadata?.showAlert === true; 127 }, { eager: false }); 128 129 // Listen for both cart product added and updated events 130 // Alert will only show if shouldShowAlert flag was set to true 131 events.on('cart/product/added', showCartAlert, { eager: false }); 132 events.on('cart/product/updated', showCartAlert, { eager: false }); 133})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.