Untitled
unknown
javascript
2 years ago
9.8 kB
19
Indexable
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);
}
Editor is loading...