(function () { if (typeof window.SwymStorefrontLayoutAPI === 'undefined') { window.SwymStorefrontLayoutAPI = {}; } if (typeof window.SwymStorefrontLayoutContext === 'undefined') { window.SwymStorefrontLayoutContext = {}; } if(typeof window.SwymStorefrontLayoutExtensions === 'undefined'){ window.SwymStorefrontLayoutExtensions = {}; } function isShopifyStoreRTLEnabled() { // Check dir attributes const htmlDir = document.documentElement.getAttribute('dir'); const bodyDir = document.body.getAttribute('dir'); // Check computed CSS direction const htmlCSSDir = getComputedStyle(document.documentElement).direction; const bodyCSSDir = getComputedStyle(document.body).direction; // Optional: check for common theme class const hasRTLClass = document.documentElement.classList.contains('rtl') || document.body.classList.contains('rtl') || document.querySelector('.rtl, .is-rtl') !== null; return ( htmlDir === 'rtl' || bodyDir === 'rtl' || htmlCSSDir === 'rtl' || bodyCSSDir === 'rtl' || hasRTLClass ); } const isRTL = isShopifyStoreRTLEnabled(); if (isRTL) { document.body.classList.add('swym-ccui-rtl'); } SwymStorefrontLayoutContext.ActionTypes = { ManageListItem: 'ManageListItem', ManageSflListItem: 'ManageSflListItem', AddToCollection: 'AddToCollection', CreateCollection: 'CreateCollection', EditCollection: 'EditCollection', SaveCollection: 'SaveCollection', ShareSingleWishlist: 'ShareSingleWishlist' } SwymStorefrontLayoutContext.NotificationStatusTypes = { Success: 'success', Error: 'error', Warning: 'Warning', Info: 'info', Neutral: 'neutral', Toast: 'toast' } SwymStorefrontLayoutContext.NotificationActionTypes = { AddedToWishlist: 'added-to-wishlist', AddToCollection: 'add-to-collection', AddedToCollection: 'added-to-collection', AddedToCart: 'added-to-cart' } SwymStorefrontLayoutContext.StorefrontLayoutUrls = { List: '#swym-list', Collections: '#swym-collections', CollectionList: '#swym-collection:' } SwymStorefrontLayoutContext.NotificationIconType = { SuccessIcon: 'SuccessIcon', RemoveIcon: 'RemoveIcon' } SwymStorefrontLayoutContext.LoaderViewType = { WishlistListItem: 'wishlist-list-item', CarouselListItem: 'carousel-list-item', CollectionImages: 'collection-view-images', SpinnerLoader: 'spinner-loader' } SwymStorefrontLayoutContext.StorefrontLayoutViewType = { Wishlist: 'tabWishlist', SaveForLater: 'tabSavedForLater' } SwymStorefrontLayoutContext.Tabs = [SwymStorefrontLayoutContext.StorefrontLayoutViewType.Wishlist] SwymStorefrontLayoutContext.ListItemType = { WishlistItem: 'WishlistItem', CollectionItem: 'CollectionItem', SaveForLaterItem: 'SaveForLaterItem' } SwymStorefrontLayoutContext.CommonAddtoWL = false; SwymStorefrontLayoutContext.CommonRemovefromWL = false; window._SwymAJAXCart = async function () { try { const cartComponent = document.querySelector('cart-notification') || document.querySelector('cart-drawer'); if (!cartComponent || typeof cartComponent.getSectionsToRender !== 'function') { console.warn('[Cart Refresh] Cart component or getSectionsToRender is missing.'); const modalPopup = document.querySelector("modal-popup"); if(modalPopup && window.routes.cart) { modalPopup.trigger("updateCart", { callback: () => { modalPopup.forwardEvent("openPopup", { target: "CART", url: window.routes.cart }); } }); return; } // Get current theme info const currentThemeName = window?.Shopify?.theme?.schema_name?.trim(); const currentThemeId = window?.Shopify?.theme?.theme_store_id; if (!currentThemeName) return false; // Use preset API to get theme event configuration window._swat.fetchThemePreset({ themeStoreId: currentThemeId, schemaName: currentThemeName }, (themeData) => { if (themeData?.AJAXCartEvent?.[0]?.eventName) { try { // Get event details from preset API response const event = themeData.AJAXCartEvent?.[0]; // Create event config object only with defined properties const eventConfig = {}; if (event.bubbles !== undefined) { eventConfig.bubbles = event.bubbles; } if (event.detail !== undefined) { eventConfig.detail = event.detail; } // Create and dispatch custom event with only defined properties const customEvent = new CustomEvent(event?.eventName, eventConfig); // Dispatch event on appropriate target const target = event.bubbles ? document.documentElement : document; target.dispatchEvent(customEvent); } catch (err) { console.warn(`[Cart Refresh] Failed to dispatch event for theme ${currentThemeName}`, err); } } }); return; } const sectionsToRender = cartComponent.getSectionsToRender(); const sectionIds = sectionsToRender.map(section => section.id).join(','); if (!sectionIds) { console.warn('[Cart Refresh] No section IDs found to render.'); return; } const [sectionRes, cartDataRes] = await Promise.all([ fetch(`/?sections=${sectionIds}`).then(res => res.ok ? res.json() : Promise.reject('Failed to fetch sections')), fetch('/cart.js').then(res => res.ok ? res.json() : Promise.reject('Failed to fetch cart data')) ]); const parser = new DOMParser(); // Update cart icon bubble const cartIconBubble = document.getElementById('cart-icon-bubble'); const iconHtmlDoc = parser.parseFromString(sectionRes['cart-icon-bubble'], 'text/html'); const iconBubbleContent = iconHtmlDoc.getElementById('shopify-section-cart-icon-bubble')?.innerHTML; if (cartIconBubble && iconBubbleContent) { cartIconBubble.innerHTML = iconBubbleContent; } // Update cart drawer if it exists const cartDrawer = document.querySelector('cart-drawer'); if (cartDrawer) { const drawerHtmlDoc = parser.parseFromString(sectionRes['cart-drawer'], 'text/html'); const newDrawerContent = drawerHtmlDoc.querySelector('cart-drawer')?.innerHTML; if (newDrawerContent !== undefined) { cartDrawer.classList.toggle('is-empty', cartDataRes.item_count === 0); cartDrawer.innerHTML = newDrawerContent; const overlay = cartDrawer.querySelector('#CartDrawer-Overlay'); if (overlay) { overlay.addEventListener('click', cartDrawer.close?.bind(cartDrawer)); } } } } catch (error) { console.error('[Cart Refresh] An error occurred:', error); } }; /** * Checks if the current user has permission to edit the given wishlist. * @param {Object} list - The wishlist to check. * @returns {boolean} - Returns true if the user can edit the list, otherwise false. */ SwymStorefrontLayoutContext.checkUserCanEditList = (list) => { if(!list){ window._swat?.utils.warn('list not exist to check list access'); return false; } if(list?.lty === 'sfl'){ let canEditList = SwymStorefrontLayoutContext?.sflList?.lid === list?.lid; return canEditList; } let canEditList = SwymStorefrontLayoutContext?.lists?.some((userList)=> userList?.lid === list?.lid); return canEditList; } /** * Formats a given price based on the store's money formatting settings. * @param {number} value - The price value to format. * @returns {string} - The formatted price string. */ SwymStorefrontLayoutContext.FormatPrice = (value) => { try { if (window.SwymOverrideFormatMoneyFn) { return window.SwymOverrideFormatMoneyFn(value); } if (window._swat.platform.formatMoney) { if ( window._swat.platform.currentMoneyFormat && window._swat.platform.currentMoneyFormat() ) { return window._swat.platform.formatMoney( value * 100, window._swat.platform.currentMoneyFormat() ); } } } catch (err) { window._swat?.utils.error("Error formatting price - " + value + err); } const currency = window._swat.currency; return currency + window._swat.utils.padDecimal(value); } /** * Retrieves the selected variant from a given item based on the provided variant ID. * * @param {Object} item - The item containing product data and variants. * @param {string | number} variantId - The ID of the variant to find. * @returns {Object | null} - The matching variant object if found, otherwise null. */ SwymStorefrontLayoutContext.getSelectedVariant = (item, variantId) => { return variantId? item?.productData?.variants?.find((variant) => variant?.id === variantId) || null:null; } /** * Calculates the difference in hours between the current time and a given date-time string. * @param {number} hours - The threshold hours to compare. * @param {string} dateTimeString - The stored date-time string. * @returns {boolean} - Returns true if the time difference exceeds the given hours, otherwise false. */ SwymStorefrontLayoutExtensions.calcDifferenceInHours = (hours, dateTimeString) => { try { if (!dateTimeString) { window._swat?.utils.warn("[WARNING] Date time string empty"); return; } const storedDateTime = new Date(dateTimeString?.trim()); const currentDateTime = new Date(); const differenceInMs = Math.abs(currentDateTime - storedDateTime); const differenceInHours = differenceInMs / (1000 * 60 * 60); return differenceInHours > hours; } catch (error) { window._swat?.utils.warn("[Error/Warning] while performing health check", error); return false; } } /** * Checks if a given container has a scrollbar and applies/removes a CSS class accordingly. * @param {HTMLElement} container - The container element to check. */ SwymStorefrontLayoutExtensions.checkContainerScrollbar = (container) => { try{ if(!container){ window._swat?.utils.warn("container not available for scrollbar check"); return; } if (container.scrollHeight > container.clientHeight) { container.classList.add("swym-storefront-layout-container-has-scroll"); } else { container.classList.remove("swym-storefront-layout-container-has-scroll"); } }catch(error){ window._swat?.utils.warn("invalid container for scrollbar check"); } } SwymStorefrontLayoutExtensions.refreshWishList = () => { try { SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.updateList(); } catch (error) { window._swat?.utils.error('Failed to fetch wishlist', error); } } SwymStorefrontLayoutExtensions.refreshSFLList = () => { try { SwymStorefrontLayoutAPI?.SwymSFLAsyncApis?.updateSflList(); } catch (error) { window._swat?.utils.error('Failed to fetch sfl list', error); } } /** * Defines action codes for various storefront layout events. */ SwymStorefrontLayoutExtensions.InstrumentActionCodes = { StorefrontLayoutInitialized: 701, StorefrontLayoutOpened: 702, StorefrontLayoutClosed: 703, StorefrontLayoutUserLogIn: 704, StorefrontLayoutCollectionRendered: 705, StorefrontLayoutWishlistItemsRendered: 706, StorefrontLayoutVariantChanged: 707, StorefrontLayoutItemAddedToCart: 708, StorefrontLayoutItemAddedToList: 709, StorefrontLayoutItemRemovedFromList: 710, StorefrontLayoutCollectionCreated: 711, StorefrontLayoutCollectionOpened: 712, StorefrontLayoutCollectionRenamed: 713, StorefrontLayoutCollectionDeleted: 714, StorefrontLayoutCollectionGridViewOpened: 715, StorefrontLayoutSFLInitialized: 716, StorefrontLayoutSFLItemsRendered: 717, StorefrontLayoutSFLItemAddedToList: 718, StorefrontLayoutSFLItemRemovedFromList: 719, StorefrontLayoutSFLItemMovedToCart: 720, StorefrontLayoutPageRendered: 721, StorefrontLayoutModalRendered: 722, StorefrontLayoutSideDrawerRendered: 723 }; /** * Defines UTM terms for tracking user engagement and interactions. */ SwymStorefrontLayoutExtensions.InstrumentUtmTerms = { StorefrontLayoutUiEngagement: "storefront_layout_ui_engagement", StorefrontLayoutCollectionInteractions: "storefront_layout_collection_interactions", StorefrontLayoutItemInteractions: "storefront_layout_item_interactions", StorefrontLayoutCartConversion: "storefront_layout_cart_conversion" }; /** * SwymWishlistCore Component * * This component serves as the base class for Swym Wishlist Storefront Layout, * providing the core functionality shared across different layout implementations: * - Drawer * - Page Section * - Modal * * Key Responsibilities: * - Initializes the UI and sets up event listeners for wishlist interactions. * - Handles fetching and rendering of wishlist items and collections. * - Monitors hash changes to update the UI accordingly. * - Integrates with Swym's API for wishlist actions and notifications. * - Provides a structured foundation for extending different storefront layout types. * * The component can be extended by specific layout implementations such as: * - `SwymStorefrontLayoutAsDrawer` * - `SwymStorefrontLayoutAsPageSection` * - `SwymStorefrontLayoutAsModal` */ class SwymWishlistCore extends HTMLElement { constructor() { super(); this.elements = {}; this.initUi(); } initUi(){ this.setAttribute('id', 'swym-storefront-layout'); window.addEventListener('hashchange', ()=>{ this.checkForSwymDrawerHash(); }); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.LayoutInitialized, () => { SwymStorefrontLayoutContext?.swat.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutInitialized, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); this.checkForSwymDrawerHash(); this.initElement(); SwymStorefrontLayoutContext?.swat.evtLayer.addEventListener(SwymStorefrontLayoutContext?.swat.JSEvents.StringsLoaded, (event) => { this.loadStrings(); }); this.attachSwymEventListeners(SwymStorefrontLayoutContext.swat); this.instrumentLayoutType(); }); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.WishlistFetched, () => { this.checkForSwymDrawerHash(); }) } instrumentLayoutType(){ let StorefrontLayoutType = SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutType; if(StorefrontLayoutType === 'as-drawer'){ SwymStorefrontLayoutContext?.swat.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutSideDrawerRendered, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); }else if(StorefrontLayoutType === 'as-modal'){ SwymStorefrontLayoutContext?.swat.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutModalRendered, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); }else if(StorefrontLayoutType === 'as-section'){ SwymStorefrontLayoutContext?.swat.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutPageRendered, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); } } reRenderStorefrontLayoutUI() { this.reRenderUI(); this.initElement(); } initElement(){ SwymStorefrontLayoutExtensions.SwymStorefrontLayout = document.querySelector('swym-storefront-layout #swym-storefront-layout'); SwymStorefrontLayoutExtensions.SwymStorefrontLayoutActions = document.querySelector('#swym-storefront-layout-actions') || document.querySelector('#swym-storefront-layout-actions-target-page'); SwymStorefrontLayoutExtensions.SwymStorefrontLayoutActionTooltip = document.querySelector('#swym-storefront-layout-action-tooltip') || document.querySelector('#swym-storefront-layout-action-tooltip-target-page'); SwymStorefrontLayoutExtensions.Notification = document.querySelector('swym-storefront-layout-notification'); SwymStorefrontLayoutExtensions.collectionsListComponent = document.querySelector('swym-storefront-layout-collection-list'); } loadStrings() { try { const RetailerStrings = window._swat?.retailerSettings?.Strings || {}; const { ThemeAppStorefrontLayoutTitle, ThemeAppStorefrontLayoutAddToCartCTA, ThemeAppStorefrontLayoutAddedToCartCTA, ThemeAppStorefrontLayoutAddingToCartCTA, ThemeAppStorefrontLayoutSoldoutCTA, ThemeAppStorefrontLayoutViewCartCTA, ThemeAppStorefrontLayoutMoveToCartCTA, ThemeAppStorefrontLayoutMoveingToCartCTA, ThemeAppStorefrontLayoutRemoveSFLItemCTA, ThemeAppStorefrontLayoutLoginHeading, ThemeAppStorefrontLayoutLoginMessage, ThemeAppStorefrontLayoutLoginButtonCTA, ThemeAppStorefrontLayoutLoggedUserWelcomeMessage, ThemeAppStorefrontLayoutWishlistTitle, ThemeAppStorefrontLayoutWishlistInfo, ThemeAppStorefrontLayoutEmptyWishlistTitle, ThemeAppStorefrontLayoutEmptyWishlistDescription, ThemeAppStorefrontLayoutCollectionsTitle, ThemeAppStorefrontLayoutEmptyCarouselCollectionsDescription, ThemeAppStorefrontLayoutEmptyCollectionsTitle, ThemeAppStorefrontLayoutEmptyCollectionsDescription, ThemeAppStorefrontLayoutAddToCollectionsTitle, ThemeAppStorefrontLayoutAddToCollectionCTA, ThemeAppStorefrontLayoutCreateCollectionCTA, ThemeAppStorefrontLayoutSaveNewCollectionCTA, ThemeAppStorefrontLayoutRemoveItemCTA, ThemeAppStorefrontLayoutRenameCollectionCTA, ThemeAppStorefrontLayoutDeleteCollectionCTA, ThemeAppStorefrontLayoutShareCollectionCTA, ThemeAppStorefrontLayoutSaveCollectionCTA, ThemeAppStorefrontLayoutEditCollectionCTA, ThemeAppStorefrontLayoutShareCollectionTitle, ThemeAppStorefrontLayoutShareCollectionMessage, ThemeAppStorefrontLayoutSharedCollectionMessage, ThemeAppStorefrontLayoutUpdateCollectionTitle, ThemeAppStorefrontLayoutErrorMessageListNameRequired, ThemeAppStorefrontLayoutErrorMessageListNameRequired3Char, ThemeAppStorefrontLayoutErrorMessageListNameAlreadyExist, ThemeAppStorefrontLayoutNotificationMessageItemSaved, ThemeAppStorefrontLayoutNotificationMessageItemRemoved, ThemeAppStorefrontLayoutNotificationMessageAddedToCart, ThemeAppStorefrontLayoutNotificationMessageAddedToCollection, ThemeAppStorefrontLayoutNotificationMessageCollectionSaved, ThemeAppStorefrontLayoutNotificationMessageCollectionDeleted, ThemeAppStorefrontLayoutNotificationMessageCollectionUpdated, ThemeAppStorefrontLayoutNotificationMessageCollectionUnavailable, ThemeAppStorefrontLayoutNotificationMessageSFLItemSaved, ThemeAppStorefrontLayoutNotificationMessageSFLItemRemoved, ThemeAppStorefrontLayoutNotificationMessageMovedToCart, ThemeAppStorefrontLayoutNotificationActionAddToCollection, ThemeAppStorefrontLayoutNotificationActionView, ThemeAppStorefrontLayoutNotificationActionViewCollection, ThemeAppStorefrontLayoutNotificationActionGoToCart, ThemeAppStorefrontLayoutTextItem, ThemeAppStorefrontLayoutTextItems, ThemeAppStorefrontLayoutTabWishlist, ThemeAppStorefrontLayoutTabSavedForLater, ThemeAppStorefrontLayoutSaveForLaterTitle, ThemeAppStorefrontLayoutEmptySavedForLaterTitle, ThemeAppStorefrontLayoutEmptySavedForLaterDescription, VariantSelectorBtnText } = RetailerStrings; SwymStorefrontLayoutContext.Strings = { ...SwymStorefrontLayoutContext?.Strings, ...(ThemeAppStorefrontLayoutTitle ? { 'title': ThemeAppStorefrontLayoutTitle } : {}), ...(VariantSelectorBtnText ? { 'VariantSelectorBtnText': VariantSelectorBtnText } : {}), ...(ThemeAppStorefrontLayoutAddToCartCTA ? { 'addToCart': ThemeAppStorefrontLayoutAddToCartCTA } : {}), ...(ThemeAppStorefrontLayoutAddedToCartCTA ? { 'addedToCart': ThemeAppStorefrontLayoutAddedToCartCTA } : {}), ...(ThemeAppStorefrontLayoutAddingToCartCTA ? { 'addingToCart': ThemeAppStorefrontLayoutAddingToCartCTA } : {}), ...(ThemeAppStorefrontLayoutSoldoutCTA ? { 'soldOut': ThemeAppStorefrontLayoutSoldoutCTA } : {}), ...(ThemeAppStorefrontLayoutViewCartCTA ? { 'viewCartCta': ThemeAppStorefrontLayoutViewCartCTA } : {}), ...(ThemeAppStorefrontLayoutMoveToCartCTA ? { 'moveToCartCta': ThemeAppStorefrontLayoutMoveToCartCTA } : {}), ...(ThemeAppStorefrontLayoutMoveingToCartCTA ? { 'movingToCartCta': ThemeAppStorefrontLayoutMoveingToCartCTA } : {}), ...(ThemeAppStorefrontLayoutRemoveSFLItemCTA ? { 'removeSflItemCta': ThemeAppStorefrontLayoutRemoveSFLItemCTA } : {}), ...(ThemeAppStorefrontLayoutLoginHeading ? { 'loginHeading': ThemeAppStorefrontLayoutLoginHeading } : {}), ...(ThemeAppStorefrontLayoutLoginMessage ? { 'loginText': ThemeAppStorefrontLayoutLoginMessage } : {}), ...(ThemeAppStorefrontLayoutLoginButtonCTA ? { 'loginButtonText': ThemeAppStorefrontLayoutLoginButtonCTA } : {}), ...(ThemeAppStorefrontLayoutLoggedUserWelcomeMessage ? { 'loggedUserWelcomeMessage': ThemeAppStorefrontLayoutLoggedUserWelcomeMessage } : {}), ...(ThemeAppStorefrontLayoutWishlistTitle ? { 'wishlistTitle': ThemeAppStorefrontLayoutWishlistTitle } : {}), ...(ThemeAppStorefrontLayoutWishlistInfo ? { 'wishlistInfo': ThemeAppStorefrontLayoutWishlistInfo } : {}), ...(ThemeAppStorefrontLayoutEmptyWishlistTitle ? { 'emptyWishlistTitle': ThemeAppStorefrontLayoutEmptyWishlistTitle } : {}), ...(ThemeAppStorefrontLayoutEmptyWishlistDescription ? { 'emptyWishlistDescription': ThemeAppStorefrontLayoutEmptyWishlistDescription } : {}), ...(ThemeAppStorefrontLayoutCollectionsTitle ? { 'collectionTitle': ThemeAppStorefrontLayoutCollectionsTitle } : {}), ...(ThemeAppStorefrontLayoutEmptyCarouselCollectionsDescription ? { 'emptyCarouselCollectionText': ThemeAppStorefrontLayoutEmptyCarouselCollectionsDescription } : {}), ...(ThemeAppStorefrontLayoutEmptyCollectionsTitle ? { 'emptyCollectionText': ThemeAppStorefrontLayoutEmptyCollectionsTitle } : {}), ...(ThemeAppStorefrontLayoutEmptyCollectionsDescription ? { 'emptyCollectionDescription': ThemeAppStorefrontLayoutEmptyCollectionsDescription } : {}), ...(ThemeAppStorefrontLayoutAddToCollectionsTitle ? { 'addToCollectionTitle': ThemeAppStorefrontLayoutAddToCollectionsTitle } : {}), ...(ThemeAppStorefrontLayoutAddToCollectionCTA ? { 'addToCollectionCta': ThemeAppStorefrontLayoutAddToCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutCreateCollectionCTA ? { 'createCollectionCta': ThemeAppStorefrontLayoutCreateCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutSaveNewCollectionCTA ? { 'saveNewCollectionCta': ThemeAppStorefrontLayoutSaveNewCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutRemoveItemCTA ? { 'removeItemCta': ThemeAppStorefrontLayoutRemoveItemCTA } : {}), ...(ThemeAppStorefrontLayoutRenameCollectionCTA ? { 'renameCollectionCta': ThemeAppStorefrontLayoutRenameCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutDeleteCollectionCTA ? { 'deleteCollectionCta': ThemeAppStorefrontLayoutDeleteCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutShareCollectionCTA ? { 'shareCollectionCta': ThemeAppStorefrontLayoutShareCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutSaveCollectionCTA ? { 'saveCollectionCta': ThemeAppStorefrontLayoutSaveCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutEditCollectionCTA ? { 'editCollectionCta': ThemeAppStorefrontLayoutEditCollectionCTA } : {}), ...(ThemeAppStorefrontLayoutShareCollectionTitle ? { 'shareCollectionTitle': ThemeAppStorefrontLayoutShareCollectionTitle } : {}), ...(ThemeAppStorefrontLayoutShareCollectionMessage ? { 'shareCollectionMessage': ThemeAppStorefrontLayoutShareCollectionMessage } : {}), ...(ThemeAppStorefrontLayoutSharedCollectionMessage ? { 'sharedCollectionMessage': ThemeAppStorefrontLayoutSharedCollectionMessage } : {}), ...(ThemeAppStorefrontLayoutUpdateCollectionTitle ? { 'updateCollectionTitle': ThemeAppStorefrontLayoutUpdateCollectionTitle } : {}), ...(ThemeAppStorefrontLayoutErrorMessageListNameRequired ? { 'errorMessageListNameRequired': ThemeAppStorefrontLayoutErrorMessageListNameRequired } : {}), ...(ThemeAppStorefrontLayoutErrorMessageListNameRequired3Char ? { 'errorMessageListNameRequire3Char': ThemeAppStorefrontLayoutErrorMessageListNameRequired3Char } : {}), ...(ThemeAppStorefrontLayoutErrorMessageListNameAlreadyExist ? { 'errorMessageListNameAlreadyExist': ThemeAppStorefrontLayoutErrorMessageListNameAlreadyExist } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageItemSaved ? { 'notificationMessageItemSaved': ThemeAppStorefrontLayoutNotificationMessageItemSaved } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageItemRemoved ? { 'notificationMessageItemRemoved': ThemeAppStorefrontLayoutNotificationMessageItemRemoved } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageAddedToCart ? { 'notificationMessageAddedToCart': ThemeAppStorefrontLayoutNotificationMessageAddedToCart } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageAddedToCollection ? { 'notificationMessageAddedToCollection': ThemeAppStorefrontLayoutNotificationMessageAddedToCollection } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageCollectionSaved ? { 'notificationMessageCollectionSaved': ThemeAppStorefrontLayoutNotificationMessageCollectionSaved } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageCollectionDeleted ? { 'notificationMessageCollectionDeleted': ThemeAppStorefrontLayoutNotificationMessageCollectionDeleted } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageCollectionUpdated ? { 'notificationMessageCollectionUpdated': ThemeAppStorefrontLayoutNotificationMessageCollectionUpdated } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageCollectionUnavailable ? { 'notificationMessageCollectionUnavailable': ThemeAppStorefrontLayoutNotificationMessageCollectionUnavailable } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageSFLItemSaved ? { 'notificationMessageSFLItemSaved': ThemeAppStorefrontLayoutNotificationMessageSFLItemSaved } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageSFLItemRemoved ? { 'notificationMessageSFLItemRemoved': ThemeAppStorefrontLayoutNotificationMessageSFLItemRemoved } : {}), ...(ThemeAppStorefrontLayoutNotificationMessageMovedToCart ? { 'notificationMessageMovedToCart': ThemeAppStorefrontLayoutNotificationMessageMovedToCart } : {}), ...(ThemeAppStorefrontLayoutNotificationActionAddToCollection ? { 'notificationActionAddToCollection': ThemeAppStorefrontLayoutNotificationActionAddToCollection } : {}), ...(ThemeAppStorefrontLayoutNotificationActionView ? { 'notificationActionView': ThemeAppStorefrontLayoutNotificationActionView } : {}), ...(ThemeAppStorefrontLayoutNotificationActionViewCollection ? { 'notificationActionViewCollection': ThemeAppStorefrontLayoutNotificationActionViewCollection } : {}), ...(ThemeAppStorefrontLayoutNotificationActionGoToCart ? { 'notificationActionGoToCart': ThemeAppStorefrontLayoutNotificationActionGoToCart } : {}), ...(ThemeAppStorefrontLayoutTextItem ? { 'item': ThemeAppStorefrontLayoutTextItem } : {}), ...(ThemeAppStorefrontLayoutTextItems ? { 'items': ThemeAppStorefrontLayoutTextItems } : {}), ...(ThemeAppStorefrontLayoutTabWishlist ? { 'tabWishlist': ThemeAppStorefrontLayoutTabWishlist } : {}), ...(ThemeAppStorefrontLayoutTabSavedForLater ? { 'tabSavedForLater': ThemeAppStorefrontLayoutTabSavedForLater } : {}), ...(ThemeAppStorefrontLayoutSaveForLaterTitle ? { 'savedForLaterTitle': ThemeAppStorefrontLayoutSaveForLaterTitle } : {}), ...(ThemeAppStorefrontLayoutEmptySavedForLaterTitle ? { 'emptySavedForLaterTitle': ThemeAppStorefrontLayoutEmptySavedForLaterTitle } : {}), ...(ThemeAppStorefrontLayoutEmptySavedForLaterDescription ? { 'emptySavedForLaterDescription': ThemeAppStorefrontLayoutEmptySavedForLaterDescription } : {}), } this.reRenderStorefrontLayoutUI(); } catch (error) { window._swat?.utils.error(error); } } async checkForSwymDrawerHash() { const { StorefrontLayoutType, StorefrontLayoutAsSectionPageURL } = SwymStorefrontLayoutContext?.Settings; const currentHash = window.location.hash; if(!SwymStorefrontLayoutExtensions.SwymStorefrontLayout){ SwymStorefrontLayoutExtensions.SwymStorefrontLayout = document.querySelector('swym-storefront-layout #swym-storefront-layout'); } if(!currentHash && SwymStorefrontLayoutExtensions.SwymStorefrontLayout?.isOpen){ SwymStorefrontLayoutExtensions.SwymStorefrontLayout?.close(); return; } const openWishlistPage = () => { if (StorefrontLayoutType === 'as-drawer' || StorefrontLayoutType === 'as-modal') { SwymStorefrontLayoutExtensions?.SwymStorefrontLayout?.open(); } else if (StorefrontLayoutType === 'as-section' && window.location.pathname !== StorefrontLayoutAsSectionPageURL) { const locale = window.Shopify.routes.root const formattedLocale = locale === '/' ? '' : locale.replace(/\/$/, '') window.location = `${window.location.origin}${formattedLocale}${StorefrontLayoutAsSectionPageURL}`; history.replaceState(null, '', window.location.pathname + window.location.search); } }; if (currentHash === SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.List) { SwymStorefrontLayoutExtensions?.collectionsListComponent?.closeCollectinListView(); openWishlistPage(); } else if (currentHash.startsWith(SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.CollectionList)) { const collectionId = currentHash.slice(SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.CollectionList.length); if (collectionId && SwymStorefrontLayoutContext?.collections) { const selectedCollection = SwymStorefrontLayoutContext?.collections?.find(collection => collection.lid === collectionId); if (selectedCollection) { SwymStorefrontLayoutExtensions?.collectionsListComponent?.setData({ list: selectedCollection, collections: SwymStorefrontLayoutContext?.collections }); SwymStorefrontLayoutExtensions?.collectionsListComponent?.openCollectionListView(); } else { try{ const sharedListDetails = await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.fetchListDetails(collectionId); if(sharedListDetails?.list){ let [list] = await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.fetchProductDataForLists([sharedListDetails.list], true) || []; if(list){ SwymStorefrontLayoutExtensions?.collectionsListComponent?.setData({ list, collections: SwymStorefrontLayoutContext?.collections }); SwymStorefrontLayoutExtensions?.collectionsListComponent?.openCollectionListView(); }else{ throw 'Failed to get product data for shared list'; } }else{ throw 'Collection Not Available'; } }catch(e){ SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageCollectionUnavailable, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Error, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration }); window._swat?.utils.error(`Collection detail not found for ID: ${collectionId}. It may have been deleted or is unavailable.`,e); } } openWishlistPage(); } } } /** * Attaches event listeners to Swym's event layer for handling various storefront layout interactions. * * Key Features: * - Refreshes the wishlist and save-for-later (SFL) lists when customer information is updated. * - Handles events for adding and removing items from the wishlist and SFL. * - Displays notifications for user actions such as adding to wishlist, removing items, or moving items to the cart. * - Tracks user interactions using Swym's instrumentation for analytics. * - Supports multi-list and collection features based on retailer settings. * * Events Handled: * - `customerInfoRefreshed`: Refreshes wishlist and SFL lists. * - `addedToWishlist`: Updates the wishlist and displays a notification when an item is added. * - `removedFromWishlist`: Updates the wishlist and displays a notification when an item is removed. * - `addedToSFL`: Updates the SFL list and displays a notification when an item is added. * - `removedFromSFL`: Updates the SFL list and displays a notification when an item is removed. * - `movedToCartFromSFL`: Refreshes the SFL list when an item is moved to the cart. * * Dependencies: * - SwymStorefrontLayoutExtensions: Provides methods for refreshing lists and managing notifications. * - SwymStorefrontLayoutContext: Provides context for settings, strings, and instrumentation codes. * - Swym's event layer (`swat.evtLayer`): Listens for and triggers events. */ attachSwymEventListeners(swat) { swat?.evtLayer?.addEventListener(swat?.JSEvents?.customerInfoRefreshed, ()=>{ SwymStorefrontLayoutExtensions?.refreshWishList(); if(window._swat?.retailerSettings?.SFL?.SFLFeatureEnabled){ SwymStorefrontLayoutExtensions?.refreshSFLList(); } }) //Wishlist Events swat?.evtLayer?.addEventListener(swat?.JSEvents?.addedToWishlist, (event) => { const isMultiListEnabled = swat.retailerSettings?.Wishlist?.EnableCollections; SwymStorefrontLayoutExtensions?.refreshWishList(); let image = event?.detail?.d?.iu || null; let { epi, empi } = event?.detail?.d || {}; let product = event?.detail.d; swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutItemAddedToList, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutItemInteractions, epi, empi }); if(SwymStorefrontLayoutContext?.swat?.ui?.uiRef?.settings?.UI?.WishlistShowNotification && !SwymStorefrontLayoutContext.CommonAddtoWL){ SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageItemSaved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToWishlist, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, product, image },{ showImage: true, showProgress: true }); setTimeout(()=>{ // FIXME - remove repetative code once testing is done const isOldControlCentre = !('multiple-wishlist' in (window?.SwymEnabledCommonFeatures || {})); // Determine the boolean condition based on whether it's the old or new control centre const shouldAddToCollection = isOldControlCentre ? SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection // For old control centre, use this setting : window?.SwymEnabledCommonFeatures?.['multiple-wishlist']; // For new control centre, use this feature flag if(SwymStorefrontLayoutExtensions?.Notification?.notificationData?.actionType === SwymStorefrontLayoutContext?.NotificationActionTypes?.AddedToWishlist){ SwymStorefrontLayoutExtensions?.Notification?.setMessage({ status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToWishlist, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, product, image, action: { label: shouldAddToCollection ? SwymStorefrontLayoutContext?.Strings?.notificationActionAddToCollection: SwymStorefrontLayoutContext?.Strings?.notificationActionView, onClick: () => { if(shouldAddToCollection){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutPageActions?.setData({ listId: SwymStorefrontLayoutContext?.DefaultList?.lid, list: SwymStorefrontLayoutContext?.DefaultList, item: product, collections: SwymStorefrontLayoutContext?.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.AddToCollection }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutPageActions?.openDrawer(); }else{ window.location.hash = SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.List; } } } },{ showImage: true, showActionButton: true, showTitle: true }); } },3000); } if(SwymStorefrontLayoutContext.CommonAddtoWL && !SwymStorefrontLayoutContext.CommonRemovefromWL) { SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageItemSaved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToWishlist, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, product, image },{ showImage: true, showProgress: true }); SwymStorefrontLayoutContext.CommonAddtoWL = false setTimeout(() => { SwymStorefrontLayoutExtensions?.Notification?.setMessage({ status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToWishlist, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, product, image, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionView, onClick: () => { _swat.ui.open(); } } },{ showImage: true, showActionButton: true, showTitle: true }); }, 3000) } }); swat?.evtLayer?.addEventListener(swat?.JSEvents?.removedFromWishlist, (event) => { let image = event?.detail?.d?.iu || null; let { epi, empi } = event?.detail?.d || {}; const product = event?.detail.d; if( epi && empi){ SwymStorefrontLayoutExtensions?.refreshWishList(); swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutItemRemovedFromList, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutItemInteractions, epi, empi }); } if((SwymStorefrontLayoutContext.CommonRemovefromWL && SwymStorefrontLayoutContext.CommonAddtoWL) || SwymStorefrontLayoutContext.CommonRemovefromWL) { if(SwymStorefrontLayoutContext.CommonRemovefromWL && !SwymStorefrontLayoutContext.CommonAddtoWL) { SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageItemRemoved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Toast, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, image }); SwymStorefrontLayoutContext.CommonRemovefromWL = false; setTimeout(() => { SwymStorefrontLayoutExtensions?.Notification?.setMessage({ status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToWishlist, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, product, image, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionView, onClick: () => { _swat.ui.open(); } } },{ showImage: true, showActionButton: true, showTitle: true }); }, 3000) } return } SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageItemRemoved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Toast, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, image }); }); //SFL Events if(window._swat?.retailerSettings?.SFL?.SFLFeatureEnabled){ swat.evtLayer.addEventListener(swat.JSEvents.addedToSFL, (event) => { let image = event?.detail?.d?.iu || null; let title = event?.detail?.d?.dt || null; let { epi, empi } = event?.detail?.d || {}; window._swat.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutSFLItemAddedToList, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutItemInteractions, epi, empi }); SwymStorefrontLayoutExtensions?.refreshSFLList(); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageSFLItemSaved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, image, title },{ showProgress: true, showTitle: true }); }); swat.evtLayer.addEventListener(swat.JSEvents.removedFromSFL, (event) => { let image = event?.detail?.d?.iu || null; let title = event?.detail?.d?.dt || null; let { epi, empi } = event?.detail?.d || {}; window._swat.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutSFLItemRemovedFromList, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutItemInteractions, epi, empi }); SwymStorefrontLayoutExtensions?.refreshSFLList(); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageSFLItemRemoved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, image, title },{ showProgress: true, showTitle: true }); }); swat.evtLayer.addEventListener(swat.JSEvents.movedToCartFromSFL, (event) => { swat.utils.debounce(()=>{ SwymStorefrontLayoutExtensions?.refreshSFLList(); }, 1000)(); }); } } } /** * SwymStorefrontLayoutAsModal * * This class extends SwymWishlistCore to render a modal-based storefront layout for the Swym wishlist. * It manages UI rendering, event listeners, and open/close interactions. * * Features: * - Displays a modal layout for wishlist functionality. * - Supports collection carousel based on SwymStorefrontLayoutContext settings. * - Provides a close button and backdrop click handling to close the modal. * - Tracks UI engagement using Swym's instrumentation. * * Methods: * - renderUI(): Generates the modal's HTML structure. * - initUIElement(): Initializes UI elements and event listeners. * - attachDrawerEventListner(): Binds click events to close the modal. * - open(): Opens the modal and updates the UI state. * - close(): Closes the modal, removes body scroll lock, and updates history state. * - reRenderUI(): Re-renders the UI while preserving the open state. * * Usage: * */ class SwymStorefrontLayoutAsModal extends SwymWishlistCore { constructor() { super(); this.renderUI(); } renderUI() { this.innerHTML = `
` this.initUIElement(); } initUIElement() { this.elements = { drawer: this.querySelector('.swym-storefront-layout-drawer'), drawerCloseButton: this.querySelector('#swym-storefront-layout-close-drawer-button'), drawerbackdrop: this.querySelector('.swym-storefront-layout-backdrop'), collectionList: this.querySelector('#swym-storefront-layout-collection-list'), } this.attachDrawerEventListner(); } attachDrawerEventListner() { this.elements.drawerCloseButton.addEventListener('click', this.close.bind(this)); this.elements.drawerbackdrop.addEventListener('click', this.close.bind(this)); } open() { this.isOpen = true; if (this.elements && this.elements.drawer) { this.elements.drawer.classList.remove('swym-storefront-layout-hide-view'); document.body.classList.add('swym-storefront-layout-body-no-scroll'); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutOpened, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); } } close() { this.isOpen = false; if (this.elements && this.elements.drawer) { this.elements.drawer.classList.add('swym-storefront-layout-hide-view'); document.body.classList.remove('swym-storefront-layout-body-no-scroll'); history.replaceState(null, '', window.location.pathname + window.location.search); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutClosed, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); } } reRenderUI() { this.renderUI(); if (this.isOpen) { this.open(); } } } /** * SwymStorefrontLayoutAsSection * * This class extends SwymWishlistCore to render a storefront wishlist layout as a section within a specified container. * It dynamically injects the wishlist UI into the provided container element. * * Features: * - Renders the wishlist section inside a container specified by the `container-id` attribute. * - Supports collection carousel based on SwymStorefrontLayoutContext settings. * - Displays wishlist items and user login UI. * - Handles rendering errors gracefully and logs warnings if an invalid container ID is provided. * * Methods: * - renderUI(): Generates the section-based wishlist UI and injects it into the specified container. * - reRenderUI(): Re-renders the UI to reflect any updates. * * Usage: * */ class SwymStorefrontLayoutAsSection extends SwymWishlistCore { constructor() { super(); this.containerId = this.getAttribute('container-id'); this.renderUI(); } renderUI() { let { StorefrontLayoutType } = SwymStorefrontLayoutContext?.Settings; if(StorefrontLayoutType === 'as-section'){ this.listContainerToRender = null; try { this.listContainerToRender = document.getElementById(this.containerId); this.listContainerToRender.innerHTML = `
` this.listContainerToRender.classList.add('swym-storefront-layout-root-component'); } catch (error) { window._swat?.utils.warn('Invalid List Container Selector ', this.containerId); } } } reRenderUI() { this.renderUI(); } } /** * SwymStorefrontLayoutAsDrawer * * This class extends SwymWishlistCore to implement a drawer-style storefront wishlist layout. * It provides an interactive UI for users to view and manage their wishlist within a side drawer. * * Features: * - Renders a wishlist drawer that slides in/out based on user interaction. * - Supports collection carousel display based on SwymStorefrontLayoutContext settings. * - Includes login UI for authentication-related actions. * - Implements event listeners for closing the drawer via a close button or backdrop click. * - Triggers instrumentation events for UI engagement tracking. * * Methods: * - renderUI(): Generates the wishlist drawer UI and injects it into the component. * - initUIElement(): Caches DOM elements required for interactions. * - attachDrawerEventListner(): Attaches event listeners for closing the drawer. * - open(): Opens the drawer and prevents background scrolling. * - close(): Closes the drawer, restores scrolling, and updates browser history. * - reRenderUI(): Re-renders the UI and retains the open state if applicable. * * Usage: * */ class SwymStorefrontLayoutAsDrawer extends SwymWishlistCore { constructor() { super(); this.renderUI(); } renderUI() { this.innerHTML = `
` this.initUIElement(); } initUIElement() { this.elements = { drawer: this.querySelector('.swym-storefront-layout-drawer'), drawerCloseButton: this.querySelector('#swym-storefront-layout-close-drawer-button'), drawerbackdrop: this.querySelector('.swym-storefront-layout-backdrop'), collectionList: this.querySelector('#swym-storefront-layout-collection-list') } this.attachDrawerEventListner(); } attachDrawerEventListner() { this.elements.drawerCloseButton.addEventListener('click', this.close.bind(this)); this.elements.drawerbackdrop.addEventListener('click', this.close.bind(this)); } open() { this.isOpen = true; if (this.elements && this.elements.drawer) { this.elements.drawer.classList.remove('swym-storefront-layout-hide-view'); document.body.classList.add('swym-storefront-layout-body-no-scroll'); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutOpened, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); } } close() { this.isOpen = false; if (this.elements && this.elements.drawer) { this.elements.drawer.classList.add('swym-storefront-layout-hide-view'); document.body.classList.remove('swym-storefront-layout-body-no-scroll'); history.replaceState(null, '', window.location.pathname + window.location.search); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutClosed, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); } } reRenderUI() { this.renderUI(); if (this.isOpen) { this.open(); } } } /** * SwymStorefrontLayoutCollectionCarousel * * This custom Web Component is responsible for rendering a carousel of collection lists * within the storefront layout. It dynamically updates based on the provided collection * data and offers smooth scrolling functionality with navigation buttons. * * Features: * - Dynamically renders collections with images and item counts. * - Implements smooth scrolling with previous/next buttons. * - Updates button visibility based on scroll position. * - Tracks user interactions using Swym's instrumentation. * - Provides an action button for each collection with a tooltip menu. * - Supports localization via `SwymStorefrontLayoutContext?.Strings`. * * Usage: * - Call `setData({ collections })` to populate the carousel with collection data. * * Dependencies: * - `window.SwymStorefrontLayoutContext` for localized strings and configurations. * - `window._swat?.instrumentV3` for tracking user interactions. * - `SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip` for managing action tooltips. * */ class SwymStorefrontLayoutCollectionCarousel extends HTMLElement { constructor() { super(); this.collections = []; this.renderUI(); this.elements = { title: this.querySelector('.swym-storefront-layout-collection-carousel-title'), carouselContainer: this.querySelector('.swym-storefront-layout-carousel-container'), carousel: this.querySelector('#swym-storefront-layout-collection-carousel-items-container'), carouselPrevButton: this.querySelector('.swym-storefront-layout-carousel-prev-btn'), carouselNextButton: this.querySelector('.swym-storefront-layout-carousel-next-btn') } this.attachEventListner(); } setData({ collections }) { this.collections = collections; this.renderCollections(); } renderUI() { this.innerHTML = ` ` } attachEventListner() { this.elements.carouselPrevButton.addEventListener("click", () => { this.elements.carousel.scrollBy({ left: -320, behavior: "smooth", }); }); this.elements.carouselNextButton.addEventListener("click", () => { this.elements.carousel.scrollBy({ left: 320, behavior: "smooth", }); }); this.elements.carousel.addEventListener("scroll", () => this.updateCarouselButtonVisibility()); } renderCollections() { const collectionsContainer = this.querySelector('#swym-storefront-layout-collection-carousel-items-container'); if (collectionsContainer) { window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutCollectionRendered, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); if (this.collections && this.collections.length) { this.elements.carouselContainer.classList.add('swym-storefront-layout-has-items'); collectionsContainer.innerHTML = this.collections .map( (collection) => { let { lname, listcontents, lid } = collection; const listImagesHtml = Array.from({ length: 3 }) .map((_, i) => { let item = listcontents[i]; return `
${item?.iu ? `${item?.dt? item?.dt : 'product Image'}` : `
`}
`; }) .join(""); const count = listcontents.length; return `
${listImagesHtml}
${lname}
${count} ${count === 1 ? SwymStorefrontLayoutContext?.Strings?.item : SwymStorefrontLayoutContext?.Strings?.items}
` } ) .join(''); this.updateCarouselButtonVisibility(); this.attachEventListnerToCollectionGrid(); } else { this.elements.carouselContainer.classList.remove('swym-storefront-layout-has-items'); collectionsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.emptyCarouselCollectionText}
`; } let collectionCount = this.collections?.length; if(this.elements.title){ this.elements.title.innerHTML = `${SwymStorefrontLayoutContext?.Strings?.collectionTitle} ${collectionCount?`(${collectionCount})`:''}`; } } } updateCarouselButtonVisibility() { const scrollLeft = this.elements.carousel.scrollLeft; const maxScrollLeft = this.elements.carousel.scrollWidth - this.elements.carousel.clientWidth; this.elements.carouselPrevButton.style.display = scrollLeft > 0 ? "flex" : "none"; this.elements.carouselNextButton.style.display = scrollLeft < maxScrollLeft ? "flex" : "none"; } attachEventListnerToCollectionGrid() { this.collections.forEach((collection) => { const collectionItem = this.elements.carousel.querySelector( `[data-lid="${collection.lid}"]` ); collectionItem?.addEventListener("click", (event) => { window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutCollectionOpened, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); window.location.hash = SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.CollectionList + collection.lid; }); let collectionItemActionButton = collectionItem.querySelector('#swym-storefront-layout-collection-grid-item-option-button'); collectionItemActionButton?.addEventListener("click", (event) => { event.preventDefault(); event.stopPropagation(); if(SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.isTooltipOpen && SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.currentTarget === event.target){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.closeTooltip(); }else{ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: collection.lid, list: collection, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.EditCollection }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.showOnTarget(event.target); } }) }) } } /** * SwymStorefrontLayoutWishlistItem * * This custom web component represents an item in the Swym Storefront Layout Wishlist. * It dynamically renders product details, pricing, and actions such as adding to cart * or managing wishlist options. It also handles variant selection and user interactions * based on the store's configuration settings. * * Key Features: * - Displays product title, image, price, and compare-at price. * - Supports multiple product variants and allows selection. * - Handles "Add to Cart" functionality with visual feedback. * - Manages user interactions for wishlist options and collections. * - Ensures compatibility with store localization settings. * * Dependencies: * - SwymStorefrontLayoutContext: Provides store settings, strings, and utilities. * - SwymStorefrontLayoutExtensions: Handles tooltips and notifications. * - _swat.platform: Manages cart operations. * * Usage: * This component should be used within the Swym Storefront Layout to display * wishlist items effectively while maintaining a seamless user experience. */ class SwymStorefrontLayoutWishlistItem extends HTMLElement{ constructor() { super(); this.item = null; this.list = null; this.sflList = null; this.selectedVariant = null; this.collections = null; this.listItemType = SwymStorefrontLayoutContext?.ListItemType?.WishlistItem; } setData({ item, list, sflList, listItemType, collections }) { this.item = item; this.listItemType = listItemType; this.list = list; this.sflList = sflList; this.collections = collections; this.selectedVariantId = this.item?.cprops?.selectedVariantId || this.item?.epi; this.selectedVariant = SwymStorefrontLayoutContext?.getSelectedVariant(this.item, this.selectedVariantId); this.dataset.epi = this.selectedVariant?.id || this.item?.epi; this.dataset.empi = this.item?.empi; this.dataset.du = this.item?.['du-mkt'] || this.item?.du; this.renderItem(); this.initItemElements(); this.addEventListeners(); } getProductImage() { return this.selectedVariant?.featured_image?.src || this.item?.productData?.featured_image || this.item?.iu; } getFormattedPrice() { let price = this.selectedVariant?.price ?? this.item?.productData?.price; if (price === undefined || price === null) { price = this.item?.pr; } let normalizedPrice = price; if (price > 100) { normalizedPrice = price / 100; } return normalizedPrice !== undefined && normalizedPrice !== null ? SwymStorefrontLayoutContext?.FormatPrice(normalizedPrice) : ''; } getFormattedComparePrice() { let price = this.selectedVariant?.compare_at_price ?? this.item?.productData?.compare_at_price; if (price === undefined || price === null) { price = this.item?.op; } let normalizedPrice = price; if (price > 100) { normalizedPrice = price / 100; } return normalizedPrice !== undefined && normalizedPrice !== null ? SwymStorefrontLayoutContext?.FormatPrice(normalizedPrice) : ''; } getButtonState() { const isInCart = _swat.platform.isInDeviceCart(this.selectedVariant?.id); if(this.listItemType === SwymStorefrontLayoutContext?.ListItemType?.SaveForLaterItem){ return this.selectedVariant?.available ? SwymStorefrontLayoutContext?.Strings?.moveToCartCta : SwymStorefrontLayoutContext?.Strings?.soldOut; }else{ return this.selectedVariant?.available ? isInCart ? SwymStorefrontLayoutContext?.Strings?.addedToCart : SwymStorefrontLayoutContext?.Strings?.addToCart : SwymStorefrontLayoutContext?.Strings?.soldOut; } } renderVariants() { if (SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutVariantSelector && this.item?.productData?.variants?.length > 1) { return ` |
${this.item.productData.options .map( (option, index) => ` ${index==0?'':''}
${option.values .map( (value) => this.selectedVariant?.[`option${index + 1}`] === value?value:'' ) .join('')}
` ) .join('')}
`; } return ''; } renderVariantSelector() { if (SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutVariantSelector && this.item?.productData?.variants?.length > 1) { return `
${this.item.productData.options .map( (option, index) => ` ${option.values && option.values.length>1 ? ` `:''} ` ) .join('')}
`; } return ''; } renderOptionButton() { // FIXME - remove repetative code once testing is done const isOldControlCentre = !('multiple-wishlist' in (window?.SwymEnabledCommonFeatures || {})); // Determine the boolean condition based on whether it's the old or new control centre const shouldAddToCollection = isOldControlCentre ? SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection // For old control centre, use this setting : window?.SwymEnabledCommonFeatures?.['multiple-wishlist']; // For new control centre, use this feature flag if (shouldAddToCollection || SwymStorefrontLayoutContext?.checkUserCanEditList(this.list || this.sflList)) { return `
`; } return ''; } isTagAvailable(tag, product) { return window._swat && window._swat.platform?.isTagAvailable( tag, product ); } getDefaultValue(val, defaultValue) { return val ? val : defaultValue; } renderItem() { if(_swat.marketsEnabled && this.item?.['du-mkt'] == null ){ return; } const settings = window._swat.retailerSettings; const IsTagBasedActionEnabled = settings?.Wishlist?.TagBasedActionEnabled; const hideAddtoCartTag = this.getDefaultValue(settings?.UI?.HideAddToCartTag, "swym-hide-addtocart"); const disabledAddtoCartTag = this.getDefaultValue(settings?.UI?.DisabledAddtoCartTag, "swym-disabled-addtocart-with-text"); const hideProductPriceTag = this.getDefaultValue(settings?.UI?.HideProductPriceTag, "swym-hide-productprice"); const productInfo = this.item?.productData; const isHidden = IsTagBasedActionEnabled && this.isTagAvailable(hideAddtoCartTag, productInfo); const isDisabled = IsTagBasedActionEnabled && this.isTagAvailable(disabledAddtoCartTag, productInfo); const hidePrice = IsTagBasedActionEnabled && this.isTagAvailable(hideProductPriceTag, productInfo); let localizedUrl = `${this.item.du}?variant=${this.item?.epi}`; const locale = window.Shopify.routes.root if (locale !== '/') { const splitUrl = this.item.du.split('/products/') localizedUrl = `${splitUrl[0]}${locale}products/${splitUrl[1]}`; } if(this.item?.['du-mkt']){ localizedUrl = this.item['du-mkt'] } const noVariant = this.selectedVariant?.title === "Default Title"; const matchingItem = this.list?.listcontents.find(item => item.epi === this.selectedVariant?.id); const isAutoWishlist = matchingItem?.source === "auto-wishlist"; const isFirstVariant = matchingItem?.productData?.variants?.[0]?.id === this.selectedVariant?.id; let price = this.getFormattedPrice(); let comparePrice = this.getFormattedComparePrice(); this.innerHTML = `
${this.item?.dt ? this.item.dt : 'Product image'}
${this.item?.dt || ''}
${!hidePrice ? `${price}` : ''} ${comparePrice && comparePrice !== price && !hidePrice && this.selectedVariant?.compare_at_price ? `${comparePrice}`: ''} ${this.renderVariants()}
${isAutoWishlist && isFirstVariant && !noVariant && !isHidden ? `` : ``} ${ ( (isAutoWishlist && !isFirstVariant && !isHidden) || (isAutoWishlist && noVariant && !isHidden) ) ? `` : "" } ${ !isAutoWishlist && !isHidden ? ``: "" }
${this.renderOptionButton()}
`; } initItemElements(){ this.elements = { AddToCartButton: this.querySelector('[data-action="add-to-cart"]'), Image: this.querySelector('.swym-storefront-layout-grid-item-image'), Price: this.querySelector('.swym-storefront-layout-grid-item-final-price'), ComparePrice: this.querySelector('.swym-storefront-layout-grid-item-compare-price'), OptionButton: this.querySelector('#swym-storefront-layout-grid-item-option-button'), sflItemsContainer : document.querySelector(`swym-storefront-layout-sfl-container .swym-storefront-layout-items-container`), swymVariantSelector: this.querySelector('#swym-variant-selector') } } addEventListeners() { this.elements.AddToCartButton?.addEventListener('click', (event) => { if(this.listItemType === SwymStorefrontLayoutContext?.ListItemType?.SaveForLaterItem){ this.handleMoveToCart(event); }else{ this.handleAddToCart(event); } }); this.elements.OptionButton?.addEventListener('click', (event) => { if(SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.isTooltipOpen && SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.currentTarget === event.target){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.closeTooltip(); }else{ if(this.listItemType === SwymStorefrontLayoutContext?.ListItemType?.WishlistItem || this.listItemType === SwymStorefrontLayoutContext?.ListItemType?.CollectionItem){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: this.list.lid, list: this.list, item: this.item, selectedVariantId: this.selectedVariant?.id, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.ManageListItem }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.showOnTarget(event.target); }else if(this.listItemType === SwymStorefrontLayoutContext?.ListItemType?.SaveForLaterItem){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: this.sflList?.lid, list: this.sflList, item: this.item, selectedVariantId: this.selectedVariant?.id, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.ManageSflListItem }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.showOnTarget(event.target); } } }); const variantSelectors = this.querySelectorAll('.swym-storefront-layout-variant-selector'); variantSelectors?.forEach((selector) => selector.addEventListener('change', () => this.handleVariantSelectionChange()) ); this.elements.swymVariantSelector?.addEventListener('click', (event) => { window._swat.evtLayer.dispatchEvent(new CustomEvent("sw:renderVS", { detail: { variant: this.selectedVariant, lid: this.item.lid, product: { epi: this.item.epi, empi: this.item.empi, dt: this.item.dt, variants: this.item.productData.variants, du: this.item?.['du-mkt'] || this.item.du, options: this.item.productData.options } } })) }) } handleAddToCart(event) { event.preventDefault(); let button = event.target; let { empi, epi, du } = this.dataset; let originalButtonState = button.innerHTML; button.innerHTML = SwymStorefrontLayoutContext?.Strings?.addingToCart; button.setAttribute('disabled', true); _swat.replayAddToCart( { empi: +empi, du: du, qty: 1 }, +epi, (success) => { button.innerHTML = SwymStorefrontLayoutContext?.Strings?.addedToCart; button.removeAttribute('disabled'); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageAddedToCart, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToCart, image: this.item.iu, product: this.item, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionGoToCart, onClick: () => { window.location = "/cart" ; } } }, { showActionButton: true, showTitle: true }); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutItemAddedToCart, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutCartConversion, epi, empi }); window?._SwymAJAXCart(); }, (error) => { button.innerHTML = originalButtonState; button.removeAttribute('disabled'); window._swat?.utils.warn('Error :', error.description); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ title: error.description, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Error },{ showTitle: true, showImage: false }); } ); } handleMoveToCart(event) { event.preventDefault(); let button = event.target; let { empi, epi, du } = this.dataset; let originalButtonState = button.innerHTML; button.innerHTML = SwymStorefrontLayoutContext?.Strings?.movingToCartCta; button.setAttribute('disabled', true); window._swat?.replayAddToCart( { empi: +empi, du: du, qty: 1 }, +epi, async (success) => { try{ await SwymStorefrontLayoutAPI?.SwymSFLAsyncApis?.removeProductFromSFL({ epi, empi, du }, SwymStorefrontLayoutContext.sflListId); let elementDeleted = this.elements.sflItemsContainer?.querySelector(`[data-epi="${epi}"]`); elementDeleted?.remove(); window._swat?.triggerSwymEvent(window._swat?.JSEvents?.movedToCartFromSFL, this.item); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageMovedToCart, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToCart, image: this.item.iu, product: this.item, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionGoToCart, onClick: () => { window.location = "/cart" ; } } }, { showActionButton: true, showTitle: true }); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutSFLItemMovedToCart, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutCartConversion, epi, empi }); window?._SwymAJAXCart(); }catch(error){ button.innerHTML = originalButtonState; button.removeAttribute('disabled'); window._swat?.utils.warn('Error removing SFL Items:', error); } }, (error) => { button.innerHTML = originalButtonState; button.removeAttribute('disabled'); window._swat?.utils.warn('Error :', error.description); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ title: error.description, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Error },{ showTitle: true, showImage: false }); } ); } async handleVariantSelectionChange() { let selectedOptions = {}; const variantSelectors = this.querySelectorAll(".swym-storefront-layout-variant-selector"); variantSelectors.forEach((selector) => { const selectedValue = selector.value?.trim(); const optionIndex = selector.dataset.optionindex; if (selectedValue && optionIndex) { selectedOptions[`option${optionIndex}`] = selectedValue; } }); const selectedVariant = this.item.productData.variants.find((variant) => Object.entries(selectedOptions).every(([key, value]) => variant[key] === value) ); this.selectedVariant = selectedVariant; this.dataset.epi = selectedVariant.id; this.item.cprops = this.item.cprops ?? {}; this.item.cprops.selectedVariantId = selectedVariant.id; if (selectedVariant) { this.elements.AddToCartButton.textContent = this.getButtonState(); if (selectedVariant.available) { this.elements.AddToCartButton.removeAttribute('disabled'); } else { this.elements.AddToCartButton.setAttribute('disabled', true); } this.elements.Price.textContent =this.getFormattedPrice(); this.elements.ComparePrice.textContent = this.getFormattedComparePrice(); const productImage = selectedVariant?.featured_image?.src || this.item.productData?.featured_image || item.iu; this.elements.Image.src = productImage; let canEdit = (this.list?.userinfo?.em === SwymStorefrontLayoutContext?.SwymCustomerData?.email) || this.list?.canEdit; if(SwymStorefrontLayoutContext?.checkUserCanEditList(this.list)){ let { epi, empi, du } = this.item; window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutVariantChanged, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutItemInteractions, epi, empi }); await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.updateProductFromList({ epi, empi, du, cprops: { selectedVariantId: selectedVariant.id } }, this.list.lid); } } else { window._swat?.utils.warn("No matching variant found for the selected options:", selectedOptions); } } } /** * SwymStorefrontLayoutDefaultWishList * * This custom HTML element represents the default wishlist layout in the storefront. * It handles rendering wishlist items dynamically and updating the UI when new wishlist data is set. * * Features: * - Initializes with a loading state until wishlist data is available. * - Fetches product details for wishlist items and updates the UI. * - Displays an empty state when no wishlist items exist. * - Uses Swym API and instrumentation to track interactions. * * Usage: * */ class SwymStorefrontLayoutDefaultWishList extends HTMLElement { constructor() { super(); this.wishlist = null; this.collections = null; this.renderUI(); this.elements = { title: this.querySelector('.swym-storefront-layout-default-list-title'), itemsContainer: this.querySelector('#swym-storefront-layout-items-container') } } handleAddToCart(event, productDetail) { event.preventDefault(); let button = event.target; let empi = productDetail.empi; //8445423517851; let epi = productDetail.epi; //46212304076955; let du = productDetail.du; //"https://swym-test-onboarding-2-0.myshopify.com/products/the-collection-snowboard-hydrogen" let originalButtonState = button.innerHTML; button.innerHTML = SwymStorefrontLayoutContext?.Strings?.addingToCart; button.setAttribute('disabled', true); _swat.replayAddToCart( { empi: +empi, du: du, qty: 1 }, +epi, (success) => { button.innerHTML = SwymStorefrontLayoutContext?.Strings?.addedToCart; button.removeAttribute('disabled'); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageAddedToCart, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: SwymStorefrontLayoutContext?.NotificationActionTypes.AddedToCart, image: "", product: "", duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionGoToCart, onClick: () => { window.location = "/cart" ; } } }, { showActionButton: true, showTitle: true }); }, (error) => { button.innerHTML = originalButtonState; button.removeAttribute('disabled'); window._swat?.utils.warn('Error :', error.description); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ title: error.description, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Error },{ showTitle: true, showImage: false }); } ); } renderUI() { if(window.Shopify.designMode && !window.SwymCurrentStorePath) { const swymMockProductData = window.SwymSampleProduct; // Extract and clean up the IDs const swymMockProductId = swymMockProductData?.id?.replace('gid://shopify/Product/', ''); const swymMockVariantId = swymMockProductData?.variants?.edges?.[0]?.node?.id?.replace('gid://shopify/ProductVariant/', ''); // Construct the product URL using Shopify.shop const swymMockProductUrl = `https://${window.Shopify.shop}/products/${swymMockProductData?.handle}`; window.SwymParsedSampleProduct = { image: swymMockProductData?.variants?.edges?.[0]?.node?.image, empi: swymMockProductId, epi: swymMockVariantId, du: swymMockProductUrl, title: swymMockProductData?.variants?.edges?.[0]?.node?.title, price: `$${swymMockProductData?.variants?.edges?.[0]?.node?.price}` }; this.innerHTML = `
${window.SwymParsedSampleProduct?.title ? window.SwymParsedSampleProduct.title : 'Product image'}
${window.SwymParsedSampleProduct.title || ''}
${window.SwymParsedSampleProduct?.price}
` const addToCartBtn = this.querySelector(".swym-storefront-layout-grid-item-add-to-cart-button"); if (addToCartBtn) { addToCartBtn.addEventListener("click", (e) => { this.handleAddToCart(e, window.SwymParsedSampleProduct) }); } return; } // FIXME - remove repetative code once testing is done const isOldControlCentre = !('multiple-wishlist' in (window?.SwymEnabledCommonFeatures || {})); // Determine the boolean condition based on whether it's the old or new control centre const shouldAddToCollection = isOldControlCentre ? SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection // For old control centre, use this setting : window?.SwymEnabledCommonFeatures?.['multiple-wishlist']; // For new control centre, use this feature flag this.innerHTML = `
${shouldAddToCollection ? `
${SwymStorefrontLayoutContext?.Strings?.wishlistTitle}
${SwymStorefrontLayoutContext?.Strings?.wishlistInfo}
` : ''}
` } setData({ wishlist, collections }) { this.wishlist = wishlist; this.collections = collections; if (this.elements?.itemsContainer?.dataset && this.wishlist?.lid) { this.elements.itemsContainer.dataset.lid = this.wishlist.lid; } this.renderWishlist(); } async renderWishlist() { if (this.wishlist?.listcontents?.length && this.elements.itemsContainer) { window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutWishlistItemsRendered, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); const wishlistedProducts = this.wishlist?.listcontents; const fragment = document.createDocumentFragment(); wishlistedProducts.forEach((item) => { const wishlistItem = document.createElement('template'); wishlistItem.innerHTML = ``; fragment.appendChild(wishlistItem.content); }); this.elements.itemsContainer.innerHTML = ''; this.elements.itemsContainer.appendChild(fragment); wishlistedProducts.forEach((item) => { let wishlistItemElement = document.querySelector(`#swym-storefront-layout-item-${item.empi}-${item.epi}`); wishlistItemElement?.setData({ item, listItemType: SwymStorefrontLayoutContext?.ListItemType?.WishlistItem, list: this.wishlist, collections: this.collections }) }); this.elements.itemsContainer.classList.remove('swym-storefront-layout-items-has-empty'); }else{ this.elements.itemsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.emptyWishlistTitle}
${SwymStorefrontLayoutContext?.Strings?.emptyWishlistDescription}
`; this.elements.itemsContainer.classList.add('swym-storefront-layout-items-has-empty'); } let wishlistCount = this.wishlist?.listcontents?.length || 0; if(this.elements.title){ this.elements.title.innerHTML = `${SwymStorefrontLayoutContext?.Strings?.wishlistTitle} ${wishlistCount?`(${wishlistCount})`:''}`; } } } /** * SwymStorefrontLayoutCollectionList * * A custom HTML element representing a collection list within the Swym Storefront Layout. * This component is responsible for rendering a list of collections, displaying wishlist items, * handling user interactions, and managing collection data. * * Features: * - Initializes and renders the UI dynamically. * - Fetches and displays wishlist items for a collection. * - Handles user permissions for editing or saving collections. * - Provides UI elements like back navigation, action buttons, and collection details. * * Usage: * ```html * * ``` * JavaScript: * ```js * const collectionListElement = document.createElement('swym-storefront-layout-collection-list'); * collectionListElement.setData({ list: myList, collections: myCollections }); * document.body.appendChild(collectionListElement); * ``` */ class SwymStorefrontLayoutCollectionList extends HTMLElement { constructor() { super(); this.collectionList = null; this.collections = null; } initUI(){ this.renderUI(); this.elements = { backButton: this.querySelector('.swym-storefront-layout-collection-list-back-button'), itemsContainer: this.querySelector('#swym-storefront-layout-collection-list-items-container'), collectionCount: this.querySelector('.swym-storefront-layout-collection-items-count'), title: this.querySelector('.swym-storefront-layout-collection-title'), swymListActionButton: this.querySelector('#swym-storefront-layout-collection-option-button'), userAccessInfo: this.querySelector('#swym-storefront-layout-collection-list-access-info'), collectionImages: this.querySelector('#swym-storefront-layout-collection-list-images-container') } this.addEventListenerToView(); } setData({ list, collections }) { this.initUI(); this.renderLoader(); this.collectionList = list; this.collections = collections; if(this.collectionList){ this.elements.itemsContainer.dataset.lid = this.collectionList.lid; this.canEdit = SwymStorefrontLayoutContext?.checkUserCanEditList(this.collectionList); this.renderCollectionlist(); } } renderUI() { this.innerHTML = `
` } addEventListenerToView() { this.elements.backButton.addEventListener('click', () => { window.location.hash = SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.List; this.closeCollectinListView() }); this.elements.swymListActionButton?.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if(SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.isTooltipOpen && SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.currentTarget === event.target){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.closeTooltip(); }else{ if(this.canEdit){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: this.collectionList.lid, list: this.collectionList, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.EditCollection }); }else{ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: this.collectionList.lid, list: this.collectionList, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.SaveCollection }); } SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.showOnTarget(event.target); } }); } async renderCollectionlist() { if (this.collectionList && this.collectionList.listcontents && this.elements.itemsContainer) { this.elements.title.innerHTML = `
${this.collectionList.lname}
`; let itemsCount = this.collectionList?.listcontents?.length || 0; this.elements.collectionCount.innerHTML = itemsCount?`${itemsCount} ${itemsCount<=1? SwymStorefrontLayoutContext?.Strings?.item : SwymStorefrontLayoutContext?.Strings?.items }`:''; const wishlistedProducts = this.collectionList.listcontents; const fragment = document.createDocumentFragment(); this.elements.collectionImages.innerHTML = `
${wishlistedProducts.slice(0, 5).map((item, index) => item ? `Wishlist Image` : `` ).join('')}
`; if(itemsCount){ wishlistedProducts.forEach((item) => { const wishlistItem = document.createElement('template'); wishlistItem.innerHTML = ``; fragment.appendChild(wishlistItem.content); }); this.elements.itemsContainer.innerHTML = ''; this.elements.itemsContainer.appendChild(fragment); wishlistedProducts.forEach((item) => { let wishlistItemElement = document.querySelector(`#swym-storefront-layout-collection-item-${item.empi}-${item.epi}`); wishlistItemElement?.setData({ item, listItemType: SwymStorefrontLayoutContext?.ListItemType?.CollectionItem, list: this.collectionList, collections: this.collections }) }); }else{ this.renderEmptyView(); } let isUserLogged = SwymStorefrontLayoutContext?.isShopperLoggedIn; if(isUserLogged && !!this.collectionList?.userinfo?.em && this.collectionList?.userinfo?.em != SwymStorefrontLayoutContext?.SwymCustomerData?.email){ let email = this.collectionList?.userinfo?.em; this.elements.userAccessInfo.innerHTML = window._swat?.utils?.renderTemplateString( SwymStorefrontLayoutContext?.Strings?.sharedCollectionMessage, { email }); }else{ this.elements.userAccessInfo.innerHTML = ``; } this.openCollectionListView(); } } renderEmptyView(){ this.elements.itemsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.emptyCollectionText}
${SwymStorefrontLayoutContext?.Strings?.emptyCollectionDescription}
`; this.elements.itemsContainer.classList.add('swym-storefront-layout-items-has-empty'); } renderLoader(){ this.elements.collectionImages.innerHTML = ``; this.elements.itemsContainer.innerHTML = ``; } clearUI(){ this.innerHTML = ''; } openCollectionListView() { this.classList.add('swym-storefront-layout-show-collection-list'); } closeCollectinListView() { this.classList.remove('swym-storefront-layout-show-collection-list'); setTimeout(()=>{ this.clearUI(); },100); } } /** * SwymStorefrontLayoutActions - Custom Web Component * * This class extends `HTMLElement` to create and manage the UI for wishlist-related actions * in the Swym Storefront Layout. It handles functionalities such as: * * - Displaying a modal-like action panel for managing wishlist items. * - Supporting different action types (e.g., adding an item to a collection). * - Rendering the UI dynamically based on the selected list or collection. * - Managing event listeners to update UI interactions seamlessly. * * Key Features: * - Renders a structured UI layout with an image, title, options, and action buttons. * - Supports selecting collections and dynamically updates the UI upon selection. * - Implements event handling for collection management, including adding new collections. * - Uses `SwymStorefrontLayoutContext` for accessing relevant wishlist and collection data. * * Usage: * - This component is initialized automatically when added to the DOM. * - Data can be set via `setData({ listId, list, item, selectedVariantId, collections, actionType })`. * - Internal methods like `renderAddToCollection()` handle specific UI updates. * * Example: * ```html * * ``` * * Dependencies: * - Requires `SwymStorefrontLayoutContext` for accessing wishlist and collection data. * - Uses event listeners for handling user interactions. * * Notes: * - Ensures smooth UI updates with proper event handling and DOM manipulations. * - Uses query selectors to manage DOM elements efficiently. */ class SwymStorefrontLayoutActions extends HTMLElement { constructor() { super(); this.actionType = SwymStorefrontLayoutContext?.ActionTypes.ManageListItem; this.wishlistItem = null; this.collections = null; this.listId = null; this.list = null; this.selectedCollectionLid = null; this.selectedList = null; this.renderUI(); this.elements = { container: this.querySelector('.swym-storefront-layout-actions-container'), backdrop: this.querySelector('.swym-storefront-layout-action-backdrop'), closeButton: this.querySelector('.swym-storefront-layout-action-close-button'), title: this.querySelector('.swym-storefront-layout-action-title-container'), optionsContainer: this.querySelector('#swym-storefront-layout-action-options'), actionContianer: this.querySelector('#swym-storefront-layout-action-container'), wishlistItemsContainer: document.querySelector('swym-storefront-layout-wishlist-container #swym-storefront-layout-items-container'), sflItemsContainer: document.querySelector('swym-storefront-layout-sfl-container #swym-storefront-layout-items-container'), imageContainer: this.querySelector('.swym-storefront-layout-action-image-container') } this.addEventListenerToView(); } renderUI() { this.innerHTML = `
` } setData({ listId, list, item, selectedVariantId, collections, actionType = SwymStorefrontLayoutContext?.ActionTypes.ManageListItem }) { this.listId = listId; this.list = list; this.wishlistItem = item; this.collections = SwymStorefrontLayoutContext?.collections; this.currentSlectedVariantId = selectedVariantId; this.selectedCollectionLid = this.collections[0]?.lid; this.selectedList = this.collections[0] || null; this.elements.wishlistItemsContainer = document.querySelector(`.swym-storefront-layout-items-container[data-lid="${listId}"]`); this.elements.actionContianer.innerHTML = ''; if (actionType === SwymStorefrontLayoutContext?.ActionTypes.AddToCollection) { this.renderAddToCollection(); } SwymStorefrontLayoutExtensions?.checkContainerScrollbar(this.elements.optionsContainer); } async renderAddToCollection() { this.collections = SwymStorefrontLayoutContext?.collections || []; if (this.collections && this.elements.optionsContainer) { this.elements.imageContainer.innerHTML = ``; this.elements.title.innerHTML = `
${this.wishlistItem.dt}
${SwymStorefrontLayoutContext?.Strings?.addToCollectionTitle}
` this.renderCollectionOptions(); this.renderCreteCollectionAction() } } renderCollectionOptions(){ this.elements.optionsContainer.innerHTML = ` ${this.collections.map((collection) => { let { lid, lname, listcontents } = collection; let item = listcontents[0]; let count = listcontents.length; // Check if current product exists in this collection const productExists = listcontents.some(listItem => listItem.epi === this.wishlistItem.epi ); const listImagesHtml = Array.from({ length: 3 }) .map((_, i) => { let item = listcontents[i]; return `
${item?.iu ? `` : `
`}
`; }) .join(""); return `
${listImagesHtml}
${lname}
${count} ${count === 1 ? 'item' : 'items'}
` }).join('')} `; this.addMangeCollectionEventListeners(); } addMangeCollectionEventListeners() { // Track initial checkbox states this.initialCheckStates = new Map(); this.newlyCheckedLids = []; // Declare at class level this.newlyUncheckedLids = [] // Array for newly unchecked lids this.collections.forEach((collection) => { const collectionItem = this.querySelector(`[data-lid="${collection.lid}"]`); const checkbox = collectionItem?.querySelector('input[type="checkbox"]'); // Store initial checked state if (checkbox) { this.initialCheckStates.set(collection.lid, checkbox.checked); } // Add event listener specifically for the checkbox collectionItem?.addEventListener("click", async (event) => { event.preventDefault(); event.stopPropagation(); // Toggle checkbox if (checkbox) { checkbox.checked = !checkbox.checked; } const collectionSaveButton = this.querySelector('#swym-storefront-layout-save-collection-list-button'); // Clear arrays on each click this.newlyCheckedLids = []; this.newlyUncheckedLids = []; const collectionNameInput = this.querySelector('#swym-storefront-layout-create-collection-name-input'); this.collections.forEach(col => { const item = this.querySelector(`[data-lid="${col.lid}"]`); const cb = item?.querySelector('input[type="checkbox"]'); if (cb) { const wasChecked = this.initialCheckStates.get(col.lid); const isNowChecked = cb.checked; if (!wasChecked && isNowChecked) { this.newlyCheckedLids.push(col.lid); } if (wasChecked && !isNowChecked) { this.newlyUncheckedLids.push(col.lid); } } }); // Enable button if there are any changes if (collectionSaveButton) { const hasChanges = this.newlyCheckedLids.length > 0 || this.newlyUncheckedLids.length > 0; collectionSaveButton.disabled = !hasChanges; // Update button text based on changes if (hasChanges) { if (this.newlyUncheckedLids.length > 0 && this.newlyCheckedLids.length === 0) { // Only unchecking existing items if (!collectionNameInput.value.trim()) { collectionSaveButton.textContent = SwymStorefrontLayoutContext?.Strings?.removeItemCta; } else { collectionSaveButton.textContent = SwymStorefrontLayoutContext?.Strings?.updateCollectionCta || "Update List"; } } else if (this.newlyCheckedLids.length > 0 && this.newlyUncheckedLids.length === 0) { // Only adding new items collectionSaveButton.textContent = SwymStorefrontLayoutContext?.Strings?.addToCollectionCta || "Add to Collection"; } else { // Both adding and removing collectionSaveButton.textContent = SwymStorefrontLayoutContext?.Strings?.updateCollectionCta || "Update List"; } } else { if (collectionNameInput.value.trim() && collectionNameInput.value.trim().length >= 3) { collectionSaveButton.textContent = SwymStorefrontLayoutContext?.Strings?.addToCollectionCta || "Add to Collection"; collectionSaveButton.disabled = false; } } } // Toggle selected class on the parent container collectionItem.classList.toggle('swym-storefront-layout-checkbox-selected'); // Your existing logic for handling checkbox state }); // Prevent checkbox from triggering twice checkbox?.addEventListener('click', (event) => { event.stopPropagation(); }); }); } renderCreteCollectionAction(){ this.elements.actionContianer.innerHTML = `
`; this.addCreateCollectionEventListeners(); } addCreateCollectionEventListeners() { const createCollectionForm = this.querySelector('#swym-storefront-layout-create-collection-form'); const collectionNameInputContainer = this.querySelector('.swym-storefront-layout-create-collection-name-form-group'); const collectionNameInput = this.querySelector('#swym-storefront-layout-create-collection-name-input'); collectionNameInput.addEventListener("focus", () => { this.selectedCollectionLid = 'fromInput'; this.validateCollectionInputField(false); }); collectionNameInput.addEventListener("blur", () => { if (!collectionNameInput.value.trim()) { const errorMessageContainer = this.querySelector('#swym-storefront-layout-create-collection-error-message-container'); const errorMessage = this.querySelector('#swym-storefront-layout-create-collection-error-message'); // Hide error message errorMessage.innerHTML = ''; errorMessageContainer.style.display = 'none'; collectionNameInput.classList.remove('swym-storefront-layout-input-has-error'); } }); collectionNameInput.addEventListener('input', () => { this.validateCollectionInputField(); }); createCollectionForm.addEventListener('submit', async (event) => { try { event.preventDefault(); let listIdToAdd; if(this.selectedCollectionLid === 'fromInput' && collectionNameInput.value.length >= 3){ let name = collectionNameInput.value.trim(); let newList = await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.createList(name); listIdToAdd = newList.lid; this.newlyCheckedLids.push(listIdToAdd); this.selectedList = newList; } let { epi, empi, du, iu } = this.wishlistItem; // Check if item is being moved from default list // Check if item exists in default list const isInDefaultList = SwymStorefrontLayoutContext?.DefaultList?.listcontents.some( item => item.epi === epi && item.empi === empi ); if(this.newlyCheckedLids.length > 0 && this.newlyUncheckedLids.length > 0) { SwymStorefrontLayoutContext.CommonAddtoWL = true; SwymStorefrontLayoutContext.CommonRemovefromWL = true; } this.closeDrawer(); if(this.newlyCheckedLids.length > 0) { // Add to new lists SwymStorefrontLayoutContext.CommonAddtoWL = true; if(isInDefaultList) { SwymStorefrontLayoutContext.CommonRemovefromWL = true; this.newlyUncheckedLids.push(SwymStorefrontLayoutContext?.DefaultList?.lid); } await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.addProductToLists( { empi, epi, du }, this.newlyCheckedLids ); } // Handle unchecked lists (except default list) if(this.newlyUncheckedLids.length > 0) { SwymStorefrontLayoutContext.CommonRemovefromWL = true; await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.removeProductFromLists( { empi, epi, du }, this.newlyUncheckedLids ); } // Show notification if(this.newlyCheckedLids.length > 0 && this.newlyUncheckedLids.length > 0) { SwymStorefrontLayoutExtensions?.Notification?.setMessage({ title: SwymStorefrontLayoutContext?.Strings?.notificationMessageCollectionUpdated, message: SwymStorefrontLayoutContext?.Strings?.notificationMessageCollectionUpdated, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, actionType: "", duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, product: this.wishlistItem, image: iu, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionView, onClick: () => { _swat.ui.open(); }, } },{ showActionButton: true, showTitle: false, showProgress: true }); SwymStorefrontLayoutContext.CommonAddtoWL = false; SwymStorefrontLayoutContext.CommonRemovefromWL = false; } window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutCollectionCreated, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutCollectionInteractions }); } catch (error) { window._swat?.utils.error('Error on create and add product to list', error); } }); // auto focus create collection input by default when no collection available if(!this.collections?.length){ setTimeout(() => { collectionNameInput?.focus(); }, 50); } } validateCollectionInputField(showError = true){ const collectionNameInput = this.querySelector('#swym-storefront-layout-create-collection-name-input'); const collectionSaveButton = this.querySelector('#swym-storefront-layout-save-collection-list-button'); const errorMessageContainer = this.querySelector('#swym-storefront-layout-create-collection-error-message-container'); const errorMessage = this.querySelector('#swym-storefront-layout-create-collection-error-message'); const checkbox = collectionNameInput.closest('.swym-storefront-layout-create-collection-name-form-group')?.querySelector('input[type="checkbox"]'); let name = collectionNameInput.value.trim(); let error; let isNameExist = this.collections.some(collection => collection.lname.toLowerCase() === name.toLowerCase()); // Check if there are any newly checked or unchecked items const hasChanges = this.newlyCheckedLids.length > 0 || this.newlyUncheckedLids.length > 0; if (name === '') { error = SwymStorefrontLayoutContext?.Strings?.errorMessageListNameRequired; checkbox.checked = false; if (this.selectedCollectionLid === 'fromInput') { this.selectedCollectionLid = null; } } else if (name.length < 3) { error = SwymStorefrontLayoutContext?.Strings?.errorMessageListNameRequire3Char; checkbox.checked = false; if (this.selectedCollectionLid === 'fromInput') { this.selectedCollectionLid = null; } }else if(isNameExist){ error = SwymStorefrontLayoutContext?.Strings?.errorMessageListNameAlreadyExist; checkbox.checked = false; if (this.selectedCollectionLid === 'fromInput') { this.selectedCollectionLid = null; } } if (name.length > 0) { collectionNameInput.classList.add('swym-storefront-layout-input-has-value'); } else { collectionNameInput.classList.remove('swym-storefront-layout-input-has-value'); } if (error) { if(showError){ errorMessage.innerHTML = error; errorMessageContainer.style.display = 'flex'; collectionNameInput.classList.add('swym-storefront-layout-input-has-error'); } // Only disable button if there are no other changes collectionSaveButton.disabled = !hasChanges; } else { errorMessage.innerHTML = ''; errorMessageContainer.style.display = 'none'; collectionSaveButton.disabled = false; collectionNameInput.classList.remove('swym-storefront-layout-input-has-error'); // Auto check the checkbox when input is valid checkbox.checked = true; this.selectedCollectionLid = 'fromInput'; // Update button text collectionSaveButton.textContent = SwymStorefrontLayoutContext?.Strings?.addToCollectionCta; } } addEventListenerToView() { this.elements.backdrop.addEventListener('click', () => this.closeDrawer()); this.elements.closeButton.addEventListener('click', () => this.closeDrawer()); } openDrawer() { this.elements.container.classList.add('swym-storefront-layout-show-action-view'); } closeDrawer() { this.elements.container.classList.remove('swym-storefront-layout-show-action-view'); } } /** * SwymStorefrontLayoutActionTooltip * * This custom Web Component manages the action tooltip for storefront layout interactions. * It provides UI options for managing wishlist items, including adding to collections * and removing from lists. The tooltip dynamically updates based on user actions * and permissions. * * Features: * - Displays options based on the selected wishlist item. * - Supports adding/removing items from collections or lists. * - Listens for user interactions and updates the UI accordingly. * - Ensures smooth integration with SwymStorefrontLayoutContext?. * * Dependencies: * - SwymStorefrontLayoutContext: Provides necessary context for actions and permissions. * - SwymStorefrontLayoutAPI: Handles wishlist operations. * - SwymStorefrontLayoutExtensions: Manages UI interactions like opening drawers. */ class SwymStorefrontLayoutActionTooltip extends HTMLElement { constructor() { super(); this.isTooltipOpen = false; this.currentTarget = null; this.actionType = SwymStorefrontLayoutContext?.ActionTypes.ManageListItem; this.listItem = null; this.collections = null; this.listId = null; this.list = null; this.renderUI(); this.elements = { viewPortLayout: document.querySelector('.swym-storefront-layout-layout'), tooltipLayout: this.querySelector('.swym-storefront-layout-action-tooltip-layout'), optionsContainer: this.querySelector('#swym-storefront-layout-action-tooltip-options'), wishlistItemsContainer: document.querySelector('swym-storefront-layout-wishlist-container #swym-storefront-layout-items-container'), sflItemsContainer: document.querySelector('swym-storefront-layout-sfl-container #swym-storefront-layout-items-container') } this.addEventListenerToView(); } renderUI() { this.innerHTML = `
` } setData({ listId, list, item, selectedVariantId, collections, actionType = SwymStorefrontLayoutContext?.ActionTypes.ManageListItem }) { this.listId = listId; this.list = list; this.listItem = item; this.collections = SwymStorefrontLayoutContext?.collections; this.currentSlectedVariantId = selectedVariantId; this.elements.wishlistItemsContainer = document.querySelector(`swym-storefront-layout-wishlist-container .swym-storefront-layout-items-container[data-lid="${listId}"]`) this.elements.sflItemsContainer = document.querySelector(`swym-storefront-layout-sfl-container .swym-storefront-layout-items-container[data-lid="${listId}"]`); if (actionType === SwymStorefrontLayoutContext?.ActionTypes.ManageListItem) { this.renderWishlistItemOptions(); }else if(actionType === SwymStorefrontLayoutContext?.ActionTypes.EditCollection){ this.renderEditCollection(); }else if(actionType === SwymStorefrontLayoutContext?.ActionTypes.SaveCollection){ this.renderSaveCollection(); }else if(actionType === SwymStorefrontLayoutContext?.ActionTypes.ManageSflListItem){ this.renderSflListItemOptions(); } else if(actionType === SwymStorefrontLayoutContext?.ActionTypes.ShareSingleWishlist) { this.renderShareSingleWishlist(); } } async renderWishlistItemOptions() { if (this.listItem && this.elements.optionsContainer) { let selectedVariantId = this.currentSlectedVariantId || this.listItem?.epi; const selectedVariant = SwymStorefrontLayoutContext?.getSelectedVariant(this.listItem, selectedVariantId); const productImage = selectedVariant?.featured_image?.src || this.listItem.productData?.featured_image || this.listItem.iu; let canEdit = SwymStorefrontLayoutContext?.checkUserCanEditList(this.list); // FIXME - remove repetative code once testing is done const isOldControlCentre = !('multiple-wishlist' in (window?.SwymEnabledCommonFeatures || {})); // Determine the boolean condition based on whether it's the old or new control centre const shouldAddToCollection = isOldControlCentre ? SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection // For old control centre, use this setting : window?.SwymEnabledCommonFeatures?.['multiple-wishlist']; // For new control centre, use this feature flag this.elements.optionsContainer.innerHTML = ` ${shouldAddToCollection? `
${SwymStorefrontLayoutContext?.Strings?.addToCollectionCta}
` : ''} ${canEdit?`
${SwymStorefrontLayoutContext?.Strings?.removeItemCta}
`:''} `; this.addOptionEventListeners(); } } async renderShareSingleWishlist() { if(this.elements.optionsContainer) { this.elements.optionsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.shareCollectionCta}
`; this.addEditCollectionEventListeners(); } } async renderShareSFLWishlist() { if(this.elements.optionsContainer) { this.elements.optionsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.shareCollectionCta}
`; this.addShareSFLEventListeners(); } } addEditCollectionEventListeners() { const shareCollectionBtn = this.querySelector('.share-collection-button'); this.listId = SwymStorefrontLayoutContext?.sflList?.lid; shareCollectionBtn?.addEventListener('click', () => { this.shareCollection(); }); } addShareSFLEventListeners() { const shareCollectionBtn = this.querySelector('.share-collection-button'); shareCollectionBtn?.addEventListener('click', () => { this.shareCollection(SwymStorefrontLayoutContext.sflList); }); } addOptionEventListeners() { const addToCollectionBtn = this.querySelector('.add-to-collection-button'); const removeFromListBtn = this.querySelector('.remove-from-list-button'); addToCollectionBtn?.addEventListener('click', () => { this.closeTooltip(); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActions?.setData({ listId: this.listId, list: this.list, item: this.listItem, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.AddToCollection }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActions?.openDrawer(); }); removeFromListBtn?.addEventListener('click', async () => { try { this.closeTooltip(); let { epi, empi, du } = this.listItem; if(this.currentSlectedVariantId){ epi = this.currentSlectedVariantId; } await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.removeProductFromList({ epi, empi, du }, this.listId); let elementDeleted = this.elements.wishlistItemsContainer?.querySelector(`[data-epi="${epi}"]`); elementDeleted?.remove(); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutItemRemovedFromList, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutItemInteractions, epi, empi }); } catch (error) { window._swat?.utils.error('Failed to remove product from the list:', error); } }); } async renderEditCollection() { if (this.list && this.elements.optionsContainer) { this.elements.optionsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.shareCollectionCta}
${SwymStorefrontLayoutContext?.Strings?.deleteCollectionCta}
`; this.addEditCollectionEventListeners(); } } addEditCollectionEventListeners() { const shareCollectionBtn = this.querySelector('.share-collection-button'); const deleteListBtn = this.querySelector('.delete-list-button'); shareCollectionBtn?.addEventListener('click', () => { this.shareCollection(); this.closeTooltip(); }); deleteListBtn?.addEventListener('click', async () => { try { await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.deleteLists([this.list]); this.closeTooltip(); window.location.hash = SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.List; SwymStorefrontLayoutExtensions?.refreshWishList(); const collectionListView = document.querySelector('swym-storefront-layout-collection-list'); collectionListView.closeCollectinListView(); SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageCollectionDeleted, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Toast, iconType: SwymStorefrontLayoutContext?.NotificationIconType?.SuccessIcon }); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutCollectionDeleted, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutCollectionInteractions }); } catch (error) { window._swat?.utils.error('Failed to remove product from the list:', error); } }); } async renderSaveCollection() { if (this.list && this.elements.optionsContainer) { this.elements.optionsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.saveCollectionCta}
`; this.addSaveCollectionEventListeners(); } } addSaveCollectionEventListeners() { const saveCollectionBtn = this.querySelector('.save-collection-button'); saveCollectionBtn?.addEventListener('click', () => { this.closeTooltip(); this.handleSaveCollection(); }); } async shareCollection() { const activeTab = document.querySelector('.swym-storefront-layout-tab-button.swym-storefront-layout-tab-button-active'); const activeDataView = activeTab?.getAttribute('data-view'); window._swat.evtLayer.dispatchEvent(new CustomEvent('sw:renderShareWishlistUI', { detail: { lid: this.list?.lid || this.listId, lname: '', list: activeDataView === "tabSavedForLater" ? SwymStorefrontLayoutContext?.sflList : this.list, layoutType: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutType, senderName: '', // optional, activeDataView } })); } async handleSaveCollection(){ let savedList = await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.createList(this.list.lname); let itemsToAdd = this.list.listcontents.map(({ empi, epi, du }) => ({ empi, epi, du })); await SwymStorefrontLayoutAPI?.SwymWLAsyncApis?.addProductsToList(itemsToAdd, savedList.lid); window.location.hash = SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.List; SwymStorefrontLayoutExtensions?.Notification?.setMessage({ message: SwymStorefrontLayoutContext?.Strings?.notificationMessageCollectionSaved, status: SwymStorefrontLayoutContext?.NotificationStatusTypes?.Neutral, iconType: SwymStorefrontLayoutContext?.NotificationIconType?.SuccessIcon, duration: SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutNotificationDuration, action: { label: SwymStorefrontLayoutContext?.Strings?.notificationActionViewCollection, onClick: () => { window.location.hash = SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.CollectionList + savedList.lid; } } }); } addEventListenerToView() { document.addEventListener("click", (event) => { if (!this.elements.tooltipLayout.contains(event.target)) { this.closeTooltip(); } }); } async renderSflListItemOptions(){ if (this.listItem && this.elements.optionsContainer) { let selectedVariantId = this.currentSlectedVariantId || this.listItem?.epi; const selectedVariant = SwymStorefrontLayoutContext?.getSelectedVariant(this.listItem, selectedVariantId); let canEdit = SwymStorefrontLayoutContext?.checkUserCanEditList(this.list); this.elements.optionsContainer.innerHTML = ` ${canEdit?`
${SwymStorefrontLayoutContext?.Strings?.removeSflItemCta}
`:''} `; this.addSflOptionEventListeners(); } } addSflOptionEventListeners() { const removeFromListBtn = this.querySelector('.remove-from-list-button'); removeFromListBtn?.addEventListener('click', async () => { try { this.closeTooltip(); let { epi, empi, du } = this.listItem; if(this.currentSlectedVariantId){ epi = this.currentSlectedVariantId; } await SwymStorefrontLayoutAPI?.SwymSFLAsyncApis?.removeProductFromSFL({ epi, empi, du }, this.listId); let elementDeleted = this.elements.sflItemsContainer?.querySelector(`[data-epi="${epi}"]`); elementDeleted?.remove(); window._swat?.triggerSwymEvent(window._swat?.JSEvents?.removedFromSFL, this.listItem); } catch (error) { window._swat?.utils.error('Failed to remove product from the list:', error); } }); } attachEventToHideTooltip(){ window.removeEventListener("scroll", ()=> this.closeTooltip()); window.addEventListener("scroll", ()=> this.closeTooltip()); let scrollableElements = document.querySelectorAll('.swym-storefront-layout-scrollable'); scrollableElements.forEach((element) => { element.removeEventListener("scroll", ()=> this.closeTooltip()); element.addEventListener("scroll", ()=> this.closeTooltip()); }); } openTooltip() { this.isTooltipOpen = true; this.elements.tooltipLayout.classList.add('swym-storefront-layout-show-action-tooltip-view'); this.attachEventToHideTooltip(); } showOnTarget(buttonTarget){ this.closeTooltip(); let viewPort = document.querySelector('.swym-storefront-layout-layout'); const tooltip = this.elements.tooltipLayout; let buttonRect = buttonTarget.getBoundingClientRect(); let viewPortRect = viewPort?.getBoundingClientRect(); const buttonRight = buttonRect.right; const buttonLeft = buttonRect.left; const buttonLeftOnViewPort = buttonRect.left - viewPortRect?.left; const tooltipWidth = this.elements.tooltipLayout.offsetWidth const viewportWidth = viewPort.clientWidth if (buttonLeftOnViewPort > viewportWidth / 2) { tooltip.style.left = `${(buttonLeft + 2) - tooltipWidth}px` tooltip.style.top = `${buttonRect.top + 25}px` } else { tooltip.style.left = `${buttonLeft + 2 }px` tooltip.style.top = `${buttonRect.top + 25}px` } setTimeout(()=>{ this.openTooltip(); this.currentTarget = buttonTarget; },100) } closeTooltip() { this.isTooltipOpen = false; this.elements.tooltipLayout.classList.remove('swym-storefront-layout-show-action-tooltip-view'); } } /** * SwymStorefrontLayoutNotification * * A Web Component for displaying notifications in the storefront layout. * It supports customizable configurations, auto-hide functionality, * and dynamic content rendering based on user actions. * * Features: * - Supports various notification types (success, info, etc.). * - Configurable options for displaying images, titles, messages, and action buttons. * - Auto-hide functionality with configurable duration. * - Supports icons and images based on notification type. * - Integrates with `SwymStorefrontLayoutContext?.Settings` to check if notifications are enabled. * * Dependencies: * - `SwymStorefrontLayoutContext`: Provides settings and icon type references. * * Usage: * ```html * * ``` * * Example JavaScript Usage: * ```javascript * const notification = document.querySelector('swym-storefront-layout-notification'); * notification.setMessage({ * message: 'Item added to wishlist!', * status: 'success', * duration: 4000, * image: 'https://example.com/image.jpg', * action: { * label: 'View Wishlist', * onClick: () => console.log('Wishlist opened') * } * }); * ``` * * Attributes: * - `status` (String) - Defines the notification status (e.g., `success`, `info`, `error`). * - `role` (String) - Defaults to `"alert"` for accessibility. * * Methods: * - `setMessage(data, config)`: Sets notification content and displays it. * - `setNotificationConfig(config)`: Merges user-defined configuration with defaults. * - `renderUI()`: Updates the UI based on the provided notification data. * - `autoHide(duration)`: Automatically hides the notification after a specified duration. * - `showNotification()`: Displays the notification. * - `hideNotification()`: Hides the notification. * * Events: * - `click` on `.swym-storefront-layout-notification-action` triggers the provided action callback. */ class SwymStorefrontLayoutNotification extends HTMLElement { constructor() { super(); this.timeout = null; this.notificationData = null; this.defaultConfig = { showProgress: false, showImage: true, showActionButton: false, showMessage: true, showTitle: false }; this.notificationConfig = this.defaultConfig; } connectedCallback() { this.setAttribute('role', this.getAttribute('role') || 'alert'); this.setAttribute('status', this.getAttribute('status') || 'info'); } setMessage(data, config = {}) { const { actionType, message, title, status = 'success', duration = 3000, action, image, product, icon, iconType } = data; this.notificationData = { actionType, message, status, duration, action, image, product, icon, iconType, title: title || product?.dt || null }; if (SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutNotification) { this.setAttribute('status', status); this.setNotificationConfig(config); this.renderUI(); this.autoHide(duration); } } setNotificationConfig(config) { this.notificationConfig = { ...this.defaultConfig, ...config }; } renderUI() { if (!this.notificationData) return; const { message, image, iconType, title, action } = this.notificationData; const { showProgress, showImage, showActionButton, showMessage, showTitle } = this.notificationConfig; this.innerHTML = ` ${showProgress ? '
' : ''}
${showImage && (image || iconType) ?`
${ image ? `Notification Icon` : ''} ${iconType ? this.getIconHtml(iconType) : ''}
`:''}
${showTitle && title ? `
${title}
` : ''} ${showMessage && message ? `
${message}
` : ''}
${showActionButton && action ? `` : ''}
`; if (action) { this.querySelector('.swym-storefront-layout-notification-action')?.addEventListener('click', (event) => { this.hideNotification(); action.onClick?.(event); }); } } getIconHtml(iconType) { if (iconType === SwymStorefrontLayoutContext?.NotificationIconType?.SuccessIcon) { return ``; } return ''; } autoHide(duration) { clearTimeout(this.timeout); this.showNotification(); this.timeout = setTimeout(() => this.hideNotification(), duration); } showNotification() { this.classList.add('swym-storefront-layout-notification-visible'); } hideNotification() { this.classList.remove('swym-storefront-layout-notification-visible'); } } /** * SwymStorefrontLayoutLoader Web Component * * This custom element is responsible for rendering different types of loading placeholders * for the storefront layout. The type of loader is determined by the `loadertype` attribute. * * Supported loader types: * - WishlistListItem: Displays skeleton loaders for wishlist items. * - CollectionImages: Displays a placeholder for collection images. * - CarouselListItem: Displays skeleton loaders for carousel items. * - SpinnerLoader: Displays a rotating spinner for loading states. * * The component listens for attribute changes to control visibility (`loading`) * and size (`width`, `height`). * * Usage: * */ class SwymStorefrontLayoutLoader extends HTMLElement { constructor() { super(); this.renderLoader(); } renderLoader() { const loadertype = this.getAttribute('loadertype'); const buttonCta = this.getAttribute('buttonCta'); const loaderListItemHtml = Array.from({ length: 5 }) .map((_, i) => { return `
${buttonCta?``:''}
`; }) .join(""); if(loadertype === SwymStorefrontLayoutContext?.LoaderViewType.WishlistListItem){ this.parentElement.innerHTML = `${loaderListItemHtml}` }else if(loadertype === SwymStorefrontLayoutContext?.LoaderViewType.CollectionImages){ this.parentElement.innerHTML = `
` }else if(loadertype === SwymStorefrontLayoutContext?.LoaderViewType.CarouselListItem){ const loaderCollectionListHtml = Array.from({ length: 3 }) .map((_, i) => { return `
`; }) .join(""); this.parentElement.innerHTML = `${loaderCollectionListHtml}` }else if(loadertype === SwymStorefrontLayoutContext?.LoaderViewType.SpinnerLoader){ this.innerHTML = `
`; } } static get observedAttributes() { return ['loading', 'width', 'height']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'loading') { if (newValue === 'true') { this.style.display = 'flex'; } else { this.style.display = 'none'; } } else if (name == 'width') { this.style.width = newValue; } else if (name == 'height') { this.style.height = newValue; } } } /** * SwymStorefrontLayoutTitle Web Component * * This custom element is responsible for rendering the title section of the storefront layout, * including the wishlist title and total item count. * * Functionality: * - Displays the wishlist title using `SwymStorefrontLayoutContext?.Strings?.title`. * - Shows the total count of wishlist items retrieved from `SwymStorefrontLayoutContext?.lists`. * - Listens for the `WishlistFetched` event and updates the title dynamically when the wishlist is fetched. * * Usage: * */ class SwymStorefrontLayoutTitle extends HTMLElement { constructor() { super(); this.renderTitle(); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.WishlistFetched, () => { this.renderTitle(); }) } renderTitle() { const totalCount = SwymStorefrontLayoutContext?.lists?.reduce((total, list) => total + list.listcontents?.length, 0) || 0; this.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.title}
${ totalCount?`
${totalCount} ${totalCount<=1? SwymStorefrontLayoutContext?.Strings?.item : SwymStorefrontLayoutContext?.Strings?.items }
`:''}
`; } } /** * SwymStorefrontLayoutLoggedUser Web Component * * This custom element handles user authentication UI within the storefront layout. * * Functionality: * - Checks if a user is logged in using `SwymStorefrontLayoutContext?.isShopperLoggedIn`. * - Displays a login prompt if the user is not logged in (when `showLogin="true"`). * - Shows a welcome message with the user's name or email (when `showuser="true"`). * - Registers a callback in `window.SwymCallbacks` to update UI when Swym state changes. * - Tracks login button clicks via `SwymStorefrontLayoutExtensions?.InstrumentActionCodes`. * * Attributes: * - `showuser` (boolean): Determines whether to display logged-in user details. * - `showLogin` (boolean): Determines whether to display the login prompt. * * Usage: * */ class SwymStorefrontLayoutLoggedUser extends HTMLElement { constructor() { super(); if (!window.SwymCallbacks) { window.SwymCallbacks = []; } window.SwymCallbacks.push(this.swymCallbackFn.bind(this)); } swymCallbackFn(swat) { const showUser = this.getAttribute('showuser') === 'true'; const showLogin = this.getAttribute('showLogin') === 'true'; let isUserLogged = SwymStorefrontLayoutContext?.isShopperLoggedIn; if (!isUserLogged && showLogin) { this.renderLoginContainer(); } else if (isUserLogged && showUser) { this.renderUserDetails(); } } handleLoginButtonClick = (event) => { event.preventDefault(); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutUserLogIn, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); }; renderUserDetails() { let customer = SwymStorefrontLayoutContext?.SwymCustomerData; this.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.loggedUserWelcomeMessage} ${customer?.name ? customer?.name : customer?.email}
` } renderLoginContainer() { this.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.loginButtonText}
` } } class SwymStorefrontLayoutTabs extends HTMLElement { constructor() { super(); this.elements = {}; SwymStorefrontLayoutContext.SelectedLayoutView = SwymStorefrontLayoutContext?.Tabs?.[0] || null; } connectedCallback(){ this.initUI(); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.LayoutInitialized, () => { SwymStorefrontLayoutContext.Tabs = [SwymStorefrontLayoutContext?.StorefrontLayoutViewType?.Wishlist]; if(window._swat.retailerSettings.SFL?.SFLFeatureEnabled){ SwymStorefrontLayoutContext.Tabs.push(SwymStorefrontLayoutContext?.StorefrontLayoutViewType?.SaveForLater); } this.initUI(); }); } initUI(){ this.renderUI(); this.initElement(); setTimeout(() => { this.setTabSelection(this.elements.currentSelectedTab); }, 100); this.attachEvents(); } renderUI() { let tabs = SwymStorefrontLayoutContext?.Tabs; if (!tabs?.length || tabs?.length<=1){ return; } this.innerHTML = `
${tabs.map((tab)=>{ return `
${SwymStorefrontLayoutContext?.Strings?.[tab]}
` }).join('')}
`; } initElement(){ this.elements = { tabs: this.querySelectorAll('.swym-storefront-layout-tab-button'), tabIndicator: this.querySelector('.swym-storefront-layout-tab-indicator'), currentSelectedTab: this.querySelector('.swym-storefront-layout-tab-button-active'), tabContent: document.querySelector('swym-storefront-layout-tab-content') }; } setTabSelection(selectedTab){ if (!selectedTab) return; const selectedView = selectedTab.getAttribute('data-view'); if (!selectedView) return; SwymStorefrontLayoutContext.SelectedLayoutView = SwymStorefrontLayoutContext?.StorefrontLayoutViewType?.[selectedView] || SwymStorefrontLayoutContext?.StorefrontLayoutViewType?.Wishlist; this.elements.tabContainers = this.elements.tabContent?.querySelectorAll('.swym-storefront-layout-tab-container'); this.elements.tabs?.forEach(tab => tab.classList.toggle('swym-storefront-layout-tab-button-active', tab === selectedTab)); this.elements.tabContainers?.forEach(tabContainer => tabContainer.classList.toggle('swym-storefront-layout-tab-content-active', tabContainer.id === selectedView)) this.setTabIndicator(); } setTabIndicator(){ let selectedTab = this.elements.currentSelectedTab; if(selectedTab){ // Wait for next frame to ensure layout is complete requestAnimationFrame(() => { const tabCenter = selectedTab.offsetLeft + selectedTab.offsetWidth / 2; this.elements.tabIndicator?.style.setProperty( 'left', `${tabCenter - (this.elements.tabIndicator.offsetWidth ?? 0) / 2}px` ); }) } } attachEvents() { this.elements.tabs?.forEach((tab) => tab.addEventListener('click', (event) => { this.elements.currentSelectedTab = event.target; this.setTabSelection(event.target); }) ); window.addEventListener('resize', SwymStorefrontLayoutContext?.swat?.utils?.debounce(() => { this.setTabIndicator(); }, 200)); } } class SwymStorefrontLayoutTabContent extends HTMLElement { constructor() { super(); this.renderUI(); } renderUI() { this.innerHTML = ` `; } } class SwymStorefrontLayoutWishlistContainer extends HTMLElement{ constructor(){ super(); this.wishlist = null; this.collections = []; this.elements = {}; this.initUI(); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.LayoutInitialized, () => { this.attachSwymEventListeners(SwymStorefrontLayoutContext?.swat); }); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.AsyncApisInitialized, () => { SwymStorefrontLayoutExtensions?.refreshWishList(); }) this.attachEventListner(); } initUI(){ this.renderUI(); this.initElement(); } renderUI(){ const isMultipleList = SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection; // FIXME - remove repetative code once testing is done const isOldControlCentre = !('multiple-wishlist' in (window?.SwymEnabledCommonFeatures || {})); // Determine the boolean condition based on whether it's the old or new control centre const shouldAddToCollection = isOldControlCentre ? SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection // For old control centre, use this setting : window?.SwymEnabledCommonFeatures?.['multiple-wishlist']; // For new control centre, use this feature flag const isLoggedIn = window._swat?.platform?.isLoggedIn(); this.innerHTML = `
${!shouldAddToCollection && isLoggedIn ? `
` : ""}
${shouldAddToCollection ? ` ` : ''}
`; } initElement(){ this.elements = { wishlistListComponent : this.querySelector('swym-storefront-layout-default-wishlist'), collectionsCarouselComponent : this.querySelector('swym-storefront-layout-collection-carousel'), collectionsListComponent : this.querySelector('swym-storefront-layout-collection-list'), swymListActionButton: this.querySelector('#swym-storefront-layout-collection-option-button') } } attachEventListner(){ document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.WishlistFetched, () => { this.wishlist = SwymStorefrontLayoutContext?.DefaultList || null; this.collections = SwymStorefrontLayoutContext?.collections || []; this.renderWishlistAndCollections(); }) this.elements.swymListActionButton?.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if(SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.isTooltipOpen && SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.currentTarget === event.target){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.closeTooltip(); }else{ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: "", list: this.wishlist, collections: this.collections, actionType: SwymStorefrontLayoutContext?.ActionTypes.ShareSingleWishlist }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.showOnTarget(event.target); } }); } attachSwymEventListeners(swat) { swat?.evtLayer?.addEventListener(swat?.JSEvents?.storeAddedToCart, (data)=>{ swat.utils.debounce(()=>{ this.renderWishlistAndCollections(); }, 1000)(); }); swat?.evtLayer?.addEventListener(swat?.JSEvents?.storeRemovedFromCart, (data)=>{ swat.utils.debounce(()=>{ this.renderWishlistAndCollections(); }, 1000)(); }) } // Add this function to SwymStorefrontLayoutWishlistContainer class mergeWishlists() { // Get all products from all collections/wishlists const allProducts = []; if (window.SwymStorefrontLayoutContext.allLists && window.SwymStorefrontLayoutContext.allLists.length > 0) { window.SwymStorefrontLayoutContext.allLists.forEach(collection => { if (collection.listcontents && collection.listcontents.length) { // Add products from each collection, avoiding duplicates collection.listcontents.forEach(product => { const isDuplicate = allProducts.some(p => p.empi === product.empi && p.epi === product.epi ); if (!isDuplicate) { allProducts.push(product); } }); } }); } // Update default wishlist with merged products if (this.wishlist) { this.wishlist.listcontents = allProducts; } else { this.wishlist = { lid: SwymStorefrontLayoutContext?.DefaultList?.lid, listcontents: allProducts }; } // Update context SwymStorefrontLayoutContext.DefaultList = this.wishlist; } // Modify the renderWishlistAndCollections method to handle the switch renderWishlistAndCollections() { const isOldControlCentre = !('multiple-wishlist' in (window?.SwymEnabledCommonFeatures || {})); // Determine if we should show multiple wishlists const shouldShowMultiple = isOldControlCentre ? SwymStorefrontLayoutContext?.Settings?.EnableStorefrontLayoutCollection : window?.SwymEnabledCommonFeatures?.['multiple-wishlist']; // If switching from multiple to single view, merge the wishlists if (!shouldShowMultiple && window.SwymStorefrontLayoutContext?.allLists?.length > 0) { this.mergeWishlists(); } if(this.collections?.length > 0 || (this.wishlist && this.wishlist?.listcontents?.length > 0)) { this.querySelector("#swym-storefront-layout-collection-option-button")?.classList.remove('swym-hidden'); } if ( this.wishlist || this.collections) { if(window.location.hash.startsWith(SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.CollectionList)){ const collectionId = window.location.hash.slice(SwymStorefrontLayoutContext?.StorefrontLayoutUrls?.CollectionList.length); if(this.elements.collectionsListComponent && this.collections){ let selectedCollection = this.collections?.find((collection)=> collection.lid === collectionId); this.elements.collectionsListComponent.setData({ list: selectedCollection, collections: this.collections }) } } this.elements.wishlistListComponent?.setData({ wishlist: shouldShowMultiple ? this.wishlist : SwymStorefrontLayoutContext.DefaultList, collections: this.collections }); if (shouldShowMultiple && this.elements.collectionsCarouselComponent && this.collections) { this.elements.collectionsCarouselComponent?.setData({ collections: this.collections }); } } } } class SwymStorefrontLayoutSaveForLaterContainer extends HTMLElement{ constructor(){ super(); this.sflData = null; this.collections = []; this.elements = {}; this.initUI(); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.AsyncApisInitialized, () => { if(window._swat?.retailerSettings.SFL?.SFLFeatureEnabled){ this.initSfl(); } }) this.attachEventListner(); } initUI(){ this.renderUI(); this.initElement(); } renderUI(){ const isLoggedIn = window?._swat?.platform?.isLoggedIn(); this.innerHTML = `
${isLoggedIn ? `
` : "" }
`; } initElement(){ this.elements = { sflList: this.querySelector('swym-storefront-layout-sfl-list'), swymListActionButton: this.querySelector('#swym-storefront-layout-collection-option-button') } } async initSfl(){ if(!SwymStorefrontLayoutContext?.sflListId){ await SwymStorefrontLayoutAPI?.SwymSFLAsyncApis.initSFL(); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutSFLInitialized, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); } if(!SwymStorefrontLayoutContext?.sflList){ SwymStorefrontLayoutExtensions?.refreshSFLList(); } if(SwymStorefrontLayoutContext?.sflList) { this.querySelector("#swym-storefront-layout-collection-option-button")?.classList.remove("swym-hidden"); } } attachEventListner(){ this.elements.swymListActionButton?.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if(SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.isTooltipOpen && SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.currentTarget === event.target){ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.closeTooltip(); }else{ SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.setData({ listId: SwymStorefrontLayoutContext?.sflListId, list: "", collections: "", actionType: SwymStorefrontLayoutContext?.ActionTypes.ShareSingleWishlist }); SwymStorefrontLayoutExtensions?.SwymStorefrontLayoutActionTooltip?.showOnTarget(event.target); } }); } } class SwymStorefrontLayoutSaveForLaterTitle extends HTMLElement { constructor() { super(); this.renderTitle(); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.SavedForLaterFetched, () => { this.renderTitle(); }) } renderTitle() { const totalCount = SwymStorefrontLayoutContext?.sflListItems?.length || 0; this.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.savedForLaterTitle}
${ totalCount?`
${totalCount} ${totalCount<=1? SwymStorefrontLayoutContext?.Strings?.item : SwymStorefrontLayoutContext?.Strings?.items }
`:''}
`; } } class SwymStorefrontLayoutSaveForLaterList extends HTMLElement { constructor() { super(); this.renderUI(); document.addEventListener(SwymStorefrontLayoutContext?.CustomEvents?.SavedForLaterFetched, () => { this.renderSFL(); }) } renderUI() { let sflData = SwymStorefrontLayoutContext?.sflData || null; this.innerHTML = `
${sflData===null?``:''}
` this.initElements(); } initElements(){ this.elements = { title: this.querySelector('.swym-storefront-layout-sfl-list-title'), itemsContainer: this.querySelector('#swym-storefront-layout-items-container') } } async renderSFL() { const slfListItems = SwymStorefrontLayoutContext?.sflListItems; if (slfListItems?.length && this.elements.itemsContainer) { const fragment = document.createDocumentFragment(); slfListItems.forEach((item) => { const sflItem = document.createElement('template'); sflItem.innerHTML = ``; fragment.appendChild(sflItem.content); }); this.elements.itemsContainer.innerHTML = ''; this.elements.itemsContainer.appendChild(fragment); slfListItems.forEach(async (item) => { let sflItemElement = document.querySelector(`#swym-storefront-layout-sfl-item-${item.empi}-${item.epi}`); sflItemElement?.setData({ item, listItemType: SwymStorefrontLayoutContext?.ListItemType?.SaveForLaterItem, sflList: SwymStorefrontLayoutContext?.sflData?.list }) }); this.elements.itemsContainer.classList.remove('swym-storefront-layout-items-has-empty'); window._swat?.instrumentV3(SwymStorefrontLayoutExtensions?.InstrumentActionCodes?.StorefrontLayoutSFLItemsRendered, { "utm-term": SwymStorefrontLayoutExtensions?.InstrumentUtmTerms?.StorefrontLayoutUiEngagement }); }else{ this.elements.itemsContainer.innerHTML = `
${SwymStorefrontLayoutContext?.Strings?.emptySavedForLaterTitle}
${SwymStorefrontLayoutContext?.Strings?.emptySavedForLaterDescription}
${SwymStorefrontLayoutContext?.Strings?.viewCartCta}
`; this.elements.itemsContainer.classList.add('swym-storefront-layout-items-has-empty'); } } } customElements.define('swym-storefront-layout-as-modal', SwymStorefrontLayoutAsModal); if(SwymStorefrontLayoutContext?.Settings?.StorefrontLayoutType === 'as-section'){ customElements.define('swym-storefront-layout-as-section', SwymStorefrontLayoutAsSection); } customElements.define('swym-storefront-layout-as-drawer', SwymStorefrontLayoutAsDrawer); customElements.define('swym-storefront-layout-collection-carousel', SwymStorefrontLayoutCollectionCarousel); customElements.define('swym-storefront-layout-item', SwymStorefrontLayoutWishlistItem); customElements.define('swym-storefront-layout-default-wishlist', SwymStorefrontLayoutDefaultWishList); customElements.define('swym-storefront-layout-collection-list', SwymStorefrontLayoutCollectionList); customElements.define('swym-storefront-layout-actions', SwymStorefrontLayoutActions); customElements.define('swym-storefront-layout-action-tooltip', SwymStorefrontLayoutActionTooltip); customElements.define('swym-storefront-layout-notification', SwymStorefrontLayoutNotification); customElements.define('swym-storefront-layout-loader', SwymStorefrontLayoutLoader); customElements.define('swym-storefront-layout-login-user', SwymStorefrontLayoutLoggedUser); customElements.define('swym-storefront-layout-title', SwymStorefrontLayoutTitle); customElements.define('swym-storefront-layout-tabs', SwymStorefrontLayoutTabs); customElements.define('swym-storefront-layout-tab-content', SwymStorefrontLayoutTabContent); customElements.define('swym-storefront-layout-wishlist-container', SwymStorefrontLayoutWishlistContainer); customElements.define('swym-storefront-layout-sfl-container', SwymStorefrontLayoutSaveForLaterContainer); customElements.define('swym-storefront-layout-sfl-title', SwymStorefrontLayoutSaveForLaterTitle); customElements.define('swym-storefront-layout-sfl-list', SwymStorefrontLayoutSaveForLaterList); })();