Untitled
javascript
a month ago
9.8 kB
4
Indexable
Never
import Popup from '../lib/popup'; import { fetchSection } from '../lib/utils'; class ShopifyWishlist extends HTMLElement { constructor() { super(); window.shopifyWishlist = this; this.settings = JSON.parse(this.querySelector('#settings').textContent); this.hash = null; this.items = []; this.selectors = { form: '[data-shopify-wishlist-form]', button: '[data-shopify-wishlist-button]', count: '[data-wishlist-count]', products: '[data-wishlist-products]', product: '[data-product-card]', removeAll: '[data-remove-all]' }; this.initialise(); } async initialise() { document.addEventListener('submit', this.onFormSubmit.bind(this)); document.addEventListener('wishlist-hero-wishlist-sdk-ready', async () => { await this.initWishlist(); await this.getWishlistItems(); this.initButtons(); this.initWishlistPage(); }); if (typeof window.WishListHero_SDK !== 'undefined') { // If wishlist hero SDK is already loaded, trigger the event manually to leave the rest of the code intact document.dispatchEvent(new Event('wishlist-hero-wishlist-sdk-ready')); } } onRemoveAllClick(e) { if (e.target.closest(this.selectors.removeAll) == null) return; this.removeAllItems(); } initWishlistPage(container = document) { if (document.querySelector(this.selectors.products) === null) return; document.addEventListener('click', this.onRemoveAllClick.bind(this)); const productContainer = document.querySelector(this.selectors.products); if (this.items.length > 0) { const productRequests = this.items.map((item) => fetchSection( `/products/${item.ProductHandle}?section_id=wishlist-product-card&variant=${item.ProductVariantId}` ).then((section) => section) ); Promise.all(productRequests) .then((products) => { productContainer.innerHTML = ` ${products.map((product) => product.innerHTML).join('')} `; }) .then(() => { document .querySelector(this.selectors.removeAll) .classList.add('active'); productContainer .querySelectorAll('[data-product-handle]') .forEach((el, index) => { el.setAttribute('data-sal-delay', `${250 * index}`); }); this.initButtons(productContainer); window.sal.update(); }); } else { productContainer.innerHTML = this.emptyContent(); window.sal.update(); } /** * When a product is added to wishlist */ document.addEventListener('wishlistItemAdded', (e) => { if (this.count === 1) container.innerHTML = ''; container.insertAdjacentElement('afterbegin', e.detail.product); this.initButtons(container); }); /** * When a product is removed from wishlist */ document.addEventListener('wishlistItemRemoved', (e) => { if (e.detail.handle) { productContainer .querySelector(`[data-product-handle="${e.detail.handle}"]`) .remove(); } if (this.count === 0) { productContainer.innerHTML = this.emptyContent(); window.sal.update(); document .querySelector(this.selectors.removeAll) .classList.remove('active'); } }); } async onFormSubmit(e) { if (!e.target.matches(this.selectors.form)) return; e.preventDefault(); document.querySelectorAll(`#${e.target.id}`).forEach((form) => { form.querySelector(this.selectors.button).classList.add('loading'); }); if ( e.target.querySelector(this.selectors.button).classList.contains('active') ) { await this.removeWishlistItem(e.target); } else { await this.addWishlistItem(e.target); } document.querySelectorAll(`#${e.target.id}`).forEach((form) => { form.querySelector(this.selectors.button).classList.remove('loading'); }); } initButtons(container = document) { container.querySelectorAll(this.selectors.form).forEach((form) => { form.querySelector(this.selectors.button).classList.remove('loading'); form.querySelector(this.selectors.button).disabled = false; }); if (this.items && this.items.length > 0) { const ids = this.items.map((item) => item.ProductId); ids.forEach((id) => { document.querySelectorAll(`#wishlist_form_${id}`).forEach((form) => { form.querySelector(this.selectors.button).classList.add('active'); }); }); } } async initWishlist() { const hash = await window.WishListHero_SDK.GetWishListHash(true); this.hash = hash; } async getWishlistItems() { const items = await window.WishListHero_SDK.GetWishListItems(); this.items = items; this.count = items.length; } async removeAllItems() { window.WishListHero_SDK.DeleteAllWishListItems({ WishlistHash: this.hash }).then((response) => { if (!response.ok) { throw response; } const productContainer = document.querySelector(this.selectors.products); productContainer.innerHTML = this.emptyContent(); window.sal.update(); this.count = 0; document .querySelector(this.selectors.removeAll) .classList.remove('active'); }); } async addWishlistItem(form) { const formData = new FormData(form); if (form.hasAttribute('data-save-for-later')) { form.dispatchEvent(new Event('savedForLater', { bubbles: true })); } const data = { ProductId: Number(formData.get('ProductId')), ProductVariantId: Number(formData.get('ProductVariantId')), ProductName: formData.get('ProductName'), ProductImage: formData.get('ProductImage'), ProductLink: formData.get('ProductLink'), ProductHandle: formData.get('ProductHandle'), ProductPrice: Number(formData.get('ProductPrice')), ProductTitle: formData.get('ProductTitle'), WishlistHash: this.hash }; return window.WishListHero_SDK.AddWishListItem(data) .then((response) => { if (!response.ok) { throw response.error; } if (response.ok) { document .querySelectorAll(`#wishlist_form_${formData.get('ProductId')}`) .forEach((formEl) => { formEl .querySelector(this.selectors.button) .classList.add('active'); }); this.getWishlistItems(); document.dispatchEvent(new CustomEvent('savedForLater')); /** * Dispatch event for Wishlist page */ document.dispatchEvent( new CustomEvent('wishlistItemAdded', { detail: { product: form.closest(this.selectors.product) !== null ? form.closest(this.selectors.product).cloneNode(true) : null }, bubbles: true }) ); } }) .catch((error) => { this.handleWishlistError(error); }); } async removeWishlistItem(form) { const formData = new FormData(form); document .querySelectorAll(`#wishlist_form_${formData.get('ProductId')}`) .forEach((formEl) => { formEl.querySelector(this.selectors.button).classList.add('loading'); }); const data = { ProductVariantId: Number(formData.get('ProductVariantId')), WishlistHash: this.hash }; return window.WishListHero_SDK.DeleteWishListItem(data) .then((response) => { if (!response.ok) { throw response; } document .querySelectorAll(`#wishlist_form_${formData.get('ProductId')}`) .forEach((formEl) => { formEl .querySelector(this.selectors.button) .classList.remove('active'); }); this.getWishlistItems(); /** * Dispatch event for Wishlist page */ document.dispatchEvent( new CustomEvent('wishlistItemRemoved', { detail: { handle: formData.get('ProductHandle') }, bubbles: true }) ); }) .catch(() => { this.handleWishlistError(); }); } handleWishlistError() { new Popup( ` <div class="p-8"> <h3 class="mb-4 text-xl md:text-2xl">Wishlist error</h3> <p class="text-gray-500 mb-6">There was an error when trying to update your wishlist, please try again later.</p> <button class="self-start btn-black" data-popup-close>Close</button> </div> ` ); } get count() { return this._count; } set count(val) { this._count = val; document.querySelectorAll(this.selectors.count).forEach((el) => { el.textContent = val; }); if (document.querySelector('[data-wishlist-page-count') !== null) { document.querySelector( '[data-wishlist-page-count' ).textContent = `${val} ${val === 1 ? 'item' : 'items'}`; } } emptyContent() { return ` <div class="flex flex-col h-full items-center justify-center col-span-1 md:col-span-3 lg:col-span-4 py-20" data-sal="slide-down" data-sal-duration="500"> <div class="max-w-[500px] w-full mx-auto"> <h2 class="text-[18px] md:text-[32px] leading-none text-center">${this.settings.empty_subtitle}</h2> <h3 class="text-[40px] md:text-[52px] leading-none text-center mb-6">${this.settings.empty_title}</h3> </div> </div> `; } } if (!customElements.get('shopify-wishlist')) { customElements.define('shopify-wishlist', ShopifyWishlist); }