class QuickOrderListRemoveButton extends HTMLElement {
constructor() {
super();
this.addEventListener('click', (event) => {
event.preventDefault();
const quickOrderList = this.closest('quick-order-list');
quickOrderList.updateQuantity(this.dataset.index, 0);
});
}
}
customElements.define('quick-order-list-remove-button', QuickOrderListRemoveButton);
class QuickOrderListRemoveAllButton extends HTMLElement {
constructor() {
super();
const allVariants = Array.from(document.querySelectorAll('[data-variant-id]'));
const items = {};
let hasVariantsInCart = false;
this.quickOrderList = this.closest('quick-order-list');
allVariants.forEach((variant) => {
const cartQty = parseInt(variant.dataset.cartQty);
if (cartQty > 0) {
hasVariantsInCart = true;
items[parseInt(variant.dataset.variantId)] = 0;
}
});
if (!hasVariantsInCart) {
this.classList.add('hidden');
}
this.actions = {
confirm: 'confirm',
remove: 'remove',
cancel: 'cancel',
};
this.addEventListener('click', (event) => {
event.preventDefault();
if (this.dataset.action === this.actions.confirm) {
this.toggleConfirmation(false, true);
} else if (this.dataset.action === this.actions.remove) {
this.quickOrderList.updateMultipleQty(items);
this.toggleConfirmation(true, false);
} else if (this.dataset.action === this.actions.cancel) {
this.toggleConfirmation(true, false);
}
});
}
toggleConfirmation(showConfirmation, showInfo) {
this.quickOrderList
.querySelector('.quick-order-list-total__confirmation')
.classList.toggle('hidden', showConfirmation);
this.quickOrderList.querySelector('.quick-order-list-total__info').classList.toggle('hidden', showInfo);
}
}
customElements.define('quick-order-list-remove-all-button', QuickOrderListRemoveAllButton);
class QuickOrderList extends HTMLElement {
constructor() {
super();
this.cart = document.querySelector('cart-drawer');
this.actions = {
add: 'ADD',
update: 'UPDATE',
};
this.quickOrderListId = 'quick-order-list';
this.variantItemStatusElement = document.getElementById('shopping-cart-variant-item-status');
const form = this.querySelector('form');
form.addEventListener('submit', this.onSubmit.bind(this));
const debouncedOnChange = debounce((event) => {
this.onChange(event);
}, ON_CHANGE_DEBOUNCE_TIMER);
this.addEventListener('change', debouncedOnChange.bind(this));
}
cartUpdateUnsubscriber = undefined;
onSubmit(event) {
event.preventDefault();
}
connectedCallback() {
this.cartUpdateUnsubscriber = subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => {
if (event.source === this.quickOrderListId) {
return;
}
// If its another section that made the update
this.onCartUpdate();
});
this.sectionId = this.dataset.id;
}
disconnectedCallback() {
if (this.cartUpdateUnsubscriber) {
this.cartUpdateUnsubscriber();
}
}
onChange(event) {
const inputValue = parseInt(event.target.value);
const cartQuantity = parseInt(event.target.dataset.cartQuantity);
const index = event.target.dataset.index;
const name = document.activeElement.getAttribute('name');
const quantity = inputValue - cartQuantity;
if (cartQuantity > 0) {
this.updateQuantity(index, inputValue, name, this.actions.update);
} else {
this.updateQuantity(index, quantity, name, this.actions.add);
}
}
onCartUpdate() {
fetch(`${window.location.pathname}?section_id=${this.sectionId}`)
.then((response) => response.text())
.then((responseText) => {
const html = new DOMParser().parseFromString(responseText, 'text/html');
const sourceQty = html.querySelector(this.quickOrderListId);
this.innerHTML = sourceQty.innerHTML;
})
.catch((e) => {
console.error(e);
});
}
getSectionsToRender() {
return [
{
id: this.quickOrderListId,
section: document.getElementById(this.quickOrderListId).dataset.id,
selector: '.js-contents',
},
{
id: 'cart-icon-bubble',
section: 'cart-icon-bubble',
selector: '.shopify-section',
},
{
id: 'quick-order-list-live-region-text',
section: 'cart-live-region-text',
selector: '.shopify-section',
},
{
id: 'quick-order-list-total',
section: document.getElementById(this.quickOrderListId).dataset.id,
selector: '.quick-order-list__total',
},
{
id: 'CartDrawer',
selector: '#CartDrawer',
section: 'cart-drawer',
},
];
}
renderSections(parsedState) {
this.getSectionsToRender().forEach((section) => {
const sectionElement = document.getElementById(section.id);
if (sectionElement && sectionElement.parentElement && sectionElement.parentElement.classList.contains('drawer')) {
parsedState.items.length > 0
? sectionElement.parentElement.classList.remove('is-empty')
: sectionElement.parentElement.classList.add('is-empty');
setTimeout(() => {
document.querySelector('#CartDrawer-Overlay').addEventListener('click', this.cart.close.bind(this.cart));
});
}
const elementToReplace =
sectionElement && sectionElement.querySelector(section.selector)
? sectionElement.querySelector(section.selector)
: sectionElement;
if (elementToReplace) {
elementToReplace.innerHTML = this.getSectionInnerHTML(parsedState.sections[section.section], section.selector);
}
});
}
updateMultipleQty(items) {
this.querySelector('.variant-remove-total .loading__spinner').classList.remove('hidden');
const body = JSON.stringify({
updates: items,
sections: this.getSectionsToRender().map((section) => section.section),
sections_url: window.location.pathname,
});
this.updateMessage();
this.setErrorMessage();
fetch(`${routes.cart_update_url}`, { ...fetchConfig(), ...{ body } })
.then((response) => {
return response.text();
})
.then((state) => {
const parsedState = JSON.parse(state);
this.renderSections(parsedState);
})
.catch(() => {
this.setErrorMessage(window.cartStrings.error);
})
.finally(() => {
this.querySelector('.variant-remove-total .loading__spinner').classList.add('hidden');
});
}
updateQuantity(id, quantity, name, action) {
this.toggleLoading(id, true);
let routeUrl = routes.cart_change_url;
let body = JSON.stringify({
quantity,
id,
sections: this.getSectionsToRender().map((section) => section.section),
sections_url: window.location.pathname,
});
let fetchConfigType;
if (action === this.actions.add) {
fetchConfigType = 'javascript';
routeUrl = routes.cart_add_url;
body = JSON.stringify({
items: [
{
quantity: parseInt(quantity),
id: parseInt(id),
},
],
sections: this.getSectionsToRender().map((section) => section.section),
sections_url: window.location.pathname,
});
}
this.updateMessage();
this.setErrorMessage();
fetch(`${routeUrl}`, { ...fetchConfig(fetchConfigType), ...{ body } })
.then((response) => {
return response.text();
})
.then((state) => {
const parsedState = JSON.parse(state);
const quantityElement = document.getElementById(`Quantity-${id}`);
const items = document.querySelectorAll('.variant-item');
if (parsedState.description || parsedState.errors) {
const variantItem = document.querySelector(
`[id^="Variant-${id}"] .variant-item__totals.small-hide .loading__spinner`
);
variantItem.classList.add('loading__spinner--error');
this.resetQuantityInput(id, quantityElement);
if (parsedState.errors) {
this.updateLiveRegions(id, parsedState.errors);
} else {
this.updateLiveRegions(id, parsedState.description);
}
return;
}
this.classList.toggle('is-empty', parsedState.item_count === 0);
this.renderSections(parsedState);
let hasError = false;
const currentItem = parsedState.items.find((item) => item.variant_id === parseInt(id));
const updatedValue = currentItem ? currentItem.quantity : undefined;
if (updatedValue && updatedValue !== quantity) {
this.updateError(updatedValue, id);
hasError = true;
}
const variantItem = document.getElementById(`Variant-${id}`);
if (variantItem && variantItem.querySelector(`[name="${name}"]`)) {
variantItem.querySelector(`[name="${name}"]`).focus();
}
publish(PUB_SUB_EVENTS.cartUpdate, { source: this.quickOrderListId, cartData: parsedState });
if (hasError) {
this.updateMessage();
} else if (action === this.actions.add) {
this.updateMessage(parseInt(quantity));
} else if (action === this.actions.update) {
this.updateMessage(parseInt(quantity - quantityElement.dataset.cartQuantity));
} else {
this.updateMessage(-parseInt(quantityElement.dataset.cartQuantity));
}
})
.catch((error) => {
this.querySelectorAll('.loading__spinner').forEach((overlay) => overlay.classList.add('hidden'));
this.resetQuantityInput(id);
console.error(error);
this.setErrorMessage(window.cartStrings.error);
})
.finally(() => {
this.toggleLoading(id);
});
}
resetQuantityInput(id, quantityElement) {
const input = quantityElement ?? document.getElementById(`Quantity-${id}`);
input.value = input.getAttribute('value');
}
setErrorMessage(message = null) {
this.errorMessageTemplate =
this.errorMessageTemplate ??
document.getElementById(`QuickOrderListErrorTemplate-${this.sectionId}`).cloneNode(true);
const errorElements = document.querySelectorAll('.quick-order-list-error');
errorElements.forEach((errorElement) => {
errorElement.innerHTML = '';
if (!message) return;
const updatedMessageElement = this.errorMessageTemplate.cloneNode(true);
updatedMessageElement.content.querySelector('.quick-order-list-error-message').innerText = message;
errorElement.appendChild(updatedMessageElement.content);
});
}
updateMessage(quantity = null) {
const messages = this.querySelectorAll('.quick-order-list__message-text');
const icons = this.querySelectorAll('.quick-order-list__message-icon');
if (quantity === null || isNaN(quantity)) {
messages.forEach((message) => (message.innerHTML = ''));
icons.forEach((icon) => icon.classList.add('hidden'));
return;
}
const isQuantityNegative = quantity < 0;
const absQuantity = Math.abs(quantity);
const textTemplate = isQuantityNegative
? absQuantity === 1
? window.quickOrderListStrings.itemRemoved
: window.quickOrderListStrings.itemsRemoved
: quantity === 1
? window.quickOrderListStrings.itemAdded
: window.quickOrderListStrings.itemsAdded;
messages.forEach((msg) => (msg.innerHTML = textTemplate.replace('[quantity]', absQuantity)));
if (!isQuantityNegative) {
icons.forEach((i) => i.classList.remove('hidden'));
}
}
updateError(updatedValue, id) {
let message = '';
if (typeof updatedValue === 'undefined') {
message = window.cartStrings.error;
} else {
message = window.cartStrings.quantityError.replace('[quantity]', updatedValue);
}
this.updateLiveRegions(id, message);
}
updateLiveRegions(id, message) {
const variantItemErrorDesktop = document.getElementById(`Quick-order-list-item-error-desktop-${id}`);
const variantItemErrorMobile = document.getElementById(`Quick-order-list-item-error-mobile-${id}`);
if (variantItemErrorDesktop) {
variantItemErrorDesktop.querySelector('.variant-item__error-text').innerHTML = message;
variantItemErrorDesktop.closest('tr').classList.remove('hidden');
}
if (variantItemErrorMobile) variantItemErrorMobile.querySelector('.variant-item__error-text').innerHTML = message;
this.variantItemStatusElement.setAttribute('aria-hidden', true);
const cartStatus = document.getElementById('quick-order-list-live-region-text');
cartStatus.setAttribute('aria-hidden', false);
setTimeout(() => {
cartStatus.setAttribute('aria-hidden', true);
}, 1000);
}
getSectionInnerHTML(html, selector) {
return new DOMParser().parseFromString(html, 'text/html').querySelector(selector).innerHTML;
}
toggleLoading(id, enable) {
const quickOrderList = document.getElementById(this.quickOrderListId);
const quickOrderListItems = this.querySelectorAll(`#Variant-${id} .loading__spinner`);
if (enable) {
quickOrderList.classList.add('quick-order-list__container--disabled');
[...quickOrderListItems].forEach((overlay) => overlay.classList.remove('hidden'));
document.activeElement.blur();
this.variantItemStatusElement.setAttribute('aria-hidden', false);
} else {
quickOrderList.classList.remove('quick-order-list__container--disabled');
quickOrderListItems.forEach((overlay) => overlay.classList.add('hidden'));
}
}
}
customElements.define('quick-order-list', QuickOrderList);