Skip to content

Instantly share code, notes, and snippets.

@prestarocket
Forked from marioloncarek/ajax-cart.js
Created March 13, 2025 20:58
Show Gist options
  • Save prestarocket/f9133ff0fd408d589be1734f8ea33c81 to your computer and use it in GitHub Desktop.
Save prestarocket/f9133ff0fd408d589be1734f8ea33c81 to your computer and use it in GitHub Desktop.
complete add bundle to cart shopify
import {formatMoney} from '@shopify/theme-currency/currency';
import {Power3, TweenMax} from 'gsap/TweenMax';
import $ from 'jquery';
const defaults = {
cartModal: '.js-ajax-cart-modal', // classname
cartModalClose: '.js-ajax-cart-modal-close', // classname
cartDrawer: '.js-ajax-cart-drawer', // classname
cartDrawerContent: '.js-ajax-cart-drawer-content', // classname
cartDrawerClose: '.js-ajax-cart-drawer-close', // classname
cartDrawerTrigger: '.js-ajax-cart-drawer-trigger', // classname
cartOverlay: '.js-ajax-cart-overlay', // classname
cartCounter: '.js-ajax-cart-counter', // classname
addToCart: '.js-ajax-add-to-cart', // classname
addBundleToCart: '.js-ajax-add-bundle-to-cart', // classname
bundleError: '.js-ajax-bundle-error', // classname
disabledBundleButton: '.js-ajax-disabled-bundle-button', // classname
removeFromCart: '.js-ajax-remove-from-cart', // classname
removeFromCartNoDot: 'js-ajax-remove-from-cart', // classname
checkoutButton: '.js-ajax-checkout-button', // classname
bodyClass: 'is-overlay-opened', // classname
mainContent: '.js-main-content', // classname
};
const cartModal = document.querySelector(defaults.cartModal);
const cartModalClose = document.querySelector(defaults.cartModalClose);
const cartDrawer = document.querySelector(defaults.cartDrawer);
const cartDrawerContent = document.querySelector(defaults.cartDrawerContent);
const cartDrawerClose = document.querySelector(defaults.cartDrawerClose);
const cartDrawerTrigger = document.querySelector(defaults.cartDrawerTrigger);
const cartOverlay = document.querySelector(defaults.cartOverlay);
const cartCounter = document.querySelector(defaults.cartCounter);
const addToCart = document.querySelectorAll(defaults.addToCart);
const addBundleToCart = document.querySelector(defaults.addBundleToCart);
const bundleError = document.querySelector(defaults.bundleError);
const disabledBundleButton = document.querySelectorAll(defaults.disabledBundleButton);
let removeFromCart = document.querySelectorAll(defaults.removeFromCart);
const checkoutButton = document.querySelector(defaults.checkoutButton);
const htmlSelector = document.documentElement;
const mainContent = document.querySelector(defaults.mainContent);
/**
*
* @param {function} callback
*/
function fetchCart(callback) {
$.ajax({
type: 'GET',
url: '/cart.js',
dataType: 'json',
success(cart) {
onCartUpdate(cart);
currentCartItemNumber(cart);
if (cart.item_count === 0) {
renderBlankCart();
checkoutButton.classList.add('is-hidden');
} else {
renderCart(cart);
checkoutButton.classList.remove('is-hidden');
if (typeof callback === 'function') {
callback();
}
}
},
error(error) {
console.log(error);
ajaxRequestFail();
},
});
}
/**
*
* @param formID
* @param callback
*/
function addProductToCart(formID, callback) {
$.ajax({
type: 'POST',
url: '/cart/add.js',
dataType: 'json',
data: $(`#${formID}`)
.serialize(),
success(cart) {
if ((typeof callback) === 'function') {
callback(cart);
} else {
addToCartOk();
}
},
error: ajaxRequestFail,
});
}
function addAllItems(string) {
const queue = [];
const newArray = string.split(',');
for (let i = 0; i < newArray.length; i++) {
const product = newArray[i];
queue.push({
serializedForm: product,
});
}
function moveAlong() {
// If we still have requests in the queue, let's process the next one.
if (queue.length) {
const request = queue.shift();
const data = `${request.serializedForm}`;
$.ajax({
type: 'POST',
url: '/cart/add.js',
dataType: 'json',
data,
success() {
moveAlong();
},
error() {
// if it's not last one Move Along else update the cart number with the current quantity
if (queue.length) {
moveAlong();
} else {
console.log('ERROR');
}
},
});
} else {
// If the queue is empty, we add 1 to cart
addToCartOk();
}
}
moveAlong();
}
/**
*
* @param line
* @param callback
*/
function changeItem(line, callback) {
const quantity = 0;
$.ajax({
type: 'POST',
url: '/cart/change.js',
data: `quantity=${quantity}&line=${line}`,
dataType: 'json',
success(cart) {
if ((typeof callback) === 'function') {
callback(cart);
} else {
fetchCart();
}
},
error: ajaxRequestFail,
});
}
/**
*
*/
addToCart.forEach((item) => {
item.addEventListener('click', function(event) {
event.preventDefault();
const formID = this.parentNode.getAttribute('id');
console.log(formID);
addProductToCart(formID);
});
});
/**
*
*/
if (addBundleToCart) {
addBundleToCart.addEventListener('click', (event) => {
event.preventDefault();
function isAnyButtonDisabled() {
return $('.js-ajax-disabled-bundle-button:disabled').length > 0;
}
if (isAnyButtonDisabled()) {
bundleError.innerHTML = 'Error, some of the variants in bundle are sold out. Please chose available variant to add bundle to cart.';
} else {
const productForm = document.querySelectorAll('.js-ajax-bundle-form');
const productFormString = '.js-ajax-bundle-form';
let concatedFormSerializeString = '';
for (let i = 1; i <= productForm.length; i++) {
if (i < (productForm.length)) {
concatedFormSerializeString += `${$(`${productFormString}-${i}`)
.serialize()},`;
} else {
concatedFormSerializeString += `${$(`${productFormString}-${i}`)
.serialize()}`;
}
}
console.log(concatedFormSerializeString);
bundleError.innerHTML = '';
addAllItems(concatedFormSerializeString);
}
});
}
/**
*
*/
if (disabledBundleButton) {
disabledBundleButton.forEach((button) => {
button.addEventListener('click', (event) => {
event.preventDefault();
});
});
}
/**
*
* @param cart
*/
function onCartUpdate(cart) {
console.log('items in the cart?', cart.item_count);
}
/**
*
* @param cart
*/
function currentCartItemNumber(cart) {
cartCounter.innerHTML = cart.item_count;
}
/**
*
*/
function addToCartOk() {
fetchAndOpenCart();
}
/**
*
*/
function fetchAndOpenCart() {
fetchCart(() => {
openCartDrawer();
openCartOverlay();
});
}
/**
*
*/
function ajaxRequestFail() {
openFailModal();
openCartOverlay();
}
/**
*
* @param cart
*/
function renderCart(cart) {
console.log(cart);
clearCartDrawer();
cart.items.forEach((item, index) => {
const productTitle = `<a href="${item.url}" class="ajax-cart-item__title u-b4">${item.product_title}</a>`;
const productVariant = `<div class="ajax-cart-item__variant u-b4">${item.variant_title}</div>`;
const productImage = `<div class="ajax-cart-item__image" style="background-image: url(${item.image});"></div>`;
const productPrice = `<div class="ajax-cart-item__price u-b2 u-b2--medium">${formatMoney(item.line_price)}</div>`;
const productQuantity = `<div class="ajax-cart-item__quantity u-b4">Quantity: ${item.quantity}</div>`;
const productRemove = `<a class="ajax-cart-item__remove u-b4 link--underline ${defaults.removeFromCartNoDot}">Remove</a>`;
const concatProductInfo = `<div class="ajax-cart-item__single" data-line="${Number(index + 1)}"><div class="ajax-cart-item__info-wrapper">${productImage}<div class="ajax-cart-item__info">${productTitle}${productVariant}${productQuantity}</div></div>${productPrice}${productRemove}</div>`;
cartDrawerContent.innerHTML += concatProductInfo;
});
const cartSubTotal = `<div class="ajax-cart-drawer__subtotal"><span class="u-b2 u-b2--medium">Subtotal: </span><span class="ajax-cart-drawer__subtotal-price u-b2 u-b2--medium">${formatMoney(cart.total_price)}</span></div>`;
cartDrawerContent.innerHTML += cartSubTotal;
removeFromCart = document.querySelectorAll(defaults.removeFromCart);
for (let i = 0; i < removeFromCart.length; i++) {
removeFromCart[i].addEventListener('click', function() {
const line = this.parentNode.getAttribute('data-line');
console.log(line);
changeItem(line);
});
}
}
function renderBlankCart() {
console.log('Blank Cart');
clearCartDrawer();
const blankCartContent = '<div class="ajax-cart__empty u-a7 u-uppercase">Your Cart is currenty empty!</div>';
cartDrawerContent.innerHTML = blankCartContent;
}
/**
*
*/
function openCartDrawer() {
TweenMax.to(cartDrawer, 0.5, {
x: -480,
ease: Power3.easeInOut,
});
// TweenMax.to(mainContent, 0.5, {
// x: -50,
// ease: Power3.easeInOut,
// });
}
/**
*
*/
function closeCartDrawer() {
TweenMax.to(cartDrawer, 0.5, {
x: 0,
ease: Power3.easeInOut,
});
// TweenMax.to(mainContent, 0.5, {
// x: 0,
// ease: Power3.easeInOut,
// });
}
/**
*
*/
function clearCartDrawer() {
cartDrawerContent.innerHTML = '';
}
/**
*
*/
function openFailModal() {
TweenMax.to(cartModal, 0.4, {
autoAlpha: 1,
ease: Power3.easeOut,
});
}
/**
*
*/
function closeFailModal() {
TweenMax.to(cartModal, 0.2, {
autoAlpha: 0,
ease: Power3.easeOut,
});
}
/**
*
*/
function openCartOverlay() {
htmlSelector.classList.add(defaults.bodyClass);
TweenMax.to(cartOverlay, 0.5, {
autoAlpha: 1,
ease: Power3.easeInOut,
});
}
/**
*
*/
function closeCartOverlay() {
htmlSelector.classList.remove(defaults.bodyClass);
TweenMax.to(cartOverlay, 0.5, {
autoAlpha: 0,
ease: Power3.easeInOut,
});
}
/**
*
*/
cartOverlay.addEventListener('click', () => {
closeFailModal();
closeCartDrawer();
closeCartOverlay();
});
/**
*
*/
cartDrawerClose.addEventListener('click', () => {
closeCartDrawer();
closeCartOverlay();
});
/**
*
*/
cartDrawerTrigger.addEventListener('click', (event) => {
event.preventDefault();
openCartDrawer();
openCartOverlay();
});
/**
*
*/
cartModalClose.addEventListener('click', () => {
closeFailModal();
closeCartDrawer();
closeCartOverlay();
});
/**
*
*/
document.addEventListener('DOMContentLoaded', () => {
fetchCart();
});
<!--ajax cart modal-->
<div class="ajax-cart__modal js-ajax-cart-modal">
<div class="ajax-cart-modal">
<!--ajax cart modal close-->
<div class="ajax-cart-modal__close js-ajax-cart-modal-close">
{% include 'icon-close' %}
</div>
<!--end ajax cart modal close-->
<!--ajax cart modal content-->
<div class="ajax-cart-modal__content js-ajax-cart-modal-content">
<span class="u-b2 u-b2--medium">Something went wrong, please contact us</span>
</div>
<!--end ajax cart modal content-->
</div>
</div>
<!--end ajax cart modal-->
<!--ajax cart drawer-->
<div class="ajax-cart__drawer js-ajax-cart-drawer">
<div class="ajax-cart-drawer">
<!--ajax cart drawer close-->
<div class="ajax-cart-drawer__close-wrapper">
<div class="ajax-cart-drawer__close js-ajax-cart-drawer-close">
{% include 'icon-close' %}
</div>
</div>
<!--end ajax cart drawer close-->
<!--ajax cart drawer content-->
<div class="ajax-cart-drawer__content js-ajax-cart-drawer-content"></div>
<!--end ajax cart drawer content-->
<!--ajax cart drawer buttons-->
<div class="ajax-cart-drawer__buttons">
<a href="/checkout/" class="button button--black button--full-width js-button js-ajax-checkout-button">
<span>Proceed to checkout</span>
</a>
<div class="ajax-cart-drawer__cart-button">
<a href="/cart/" class="link--underline u-b2 u-b2--medium">
View cart
</a>
</div>
</div>
<!--end ajax cart drawer buttons-->
</div>
</div>
<!--end ajax cart drawer-->
<!--ajax cart overlay-->
<div class="ajax-cart__overlay js-ajax-cart-overlay"></div>
<!--end ajax cart overlay-->
$ajax-cart-header-height: 79px;
$ajax-cart-footer-height: 155px;
.ajax-cart {
&__modal {
@include center(both, fixed);
z-index: 50;
width: 100%;
max-width: 7 * $settings-grid-column-width;
margin: 0 auto;
background-color: getColor('white', 'default');
height: 220px;
display: flex;
align-items: center;
opacity: 0;
visibility: hidden;
will-change: opacity, visibility;
}
&__overlay {
position: fixed;
z-index: 30;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: getColor('black-40', 'variations');
opacity: 0;
visibility: hidden;
will-change: opacity, visibility;
}
&__drawer {
position: fixed;
z-index: 40;
right: -480px;
top: 0;
width: 480px;
height: 100%;
background: getColor('white', 'default');
will-change: transform;
}
&__empty {
border-top: 1px solid getColor('silver', 'secondary');
text-align: center;
padding: 40px 0;
}
}
.ajax-cart-modal {
position: relative;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
&__close {
position: absolute;
right: 20px;
top: 20px;
line-height: 0;
cursor: pointer;
}
&__content {
padding: 20px;
}
}
.ajax-cart-drawer {
position: relative;
height: 100%;
&__close-wrapper {
width: 100%;
height: $ajax-cart-header-height;
}
&__close {
position: absolute;
right: 30px;
top: 30px;
cursor: pointer;
line-height: 0;
font-size: 17px;
}
&__content {
padding: 0 30px;
overflow: hidden;
overflow-y: scroll;
height: calc(100% - #{$ajax-cart-footer-height} - #{$ajax-cart-header-height});
}
&__subtotal {
position: relative;
margin: 25px 0;
padding-left: 120px;
line-height: 0;
}
&__subtotal-price {
position: absolute;
right: 10px;
}
&__buttons {
width: 100%;
height: $ajax-cart-footer-height;
background: getColor('white', 'default');
padding: 20px 20px 25px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__cart-button {
text-align: center;
}
}
.ajax-cart-item {
&__single {
position: relative;
padding: 15px 0;
border-bottom: 1px solid getColor('silver', 'secondary');
&:first-child {
border-top: 1px solid getColor('silver', 'secondary');
}
}
&__info-wrapper {
display: flex;
position: relative;
}
&__info {
padding: 10px 10px 10px 30px;
max-width: 250px;
line-height: 1.18;
}
&__image {
width: 90px;
height: 110px;
background-size: contain;
background-position: center;
background-repeat: no-repeat;
}
&__title {
vertical-align: top;
}
&__variant {
position: relative;
top: 5px;
}
&__quantity {
position: absolute;
bottom: 10px;
}
&__price {
position: absolute;
bottom: 25px;
right: 10px;
}
&__remove {
position: absolute;
right: 10px;
top: 25px;
cursor: pointer;
letter-spacing: 0.2px;
}
}
/**
* Product Template Script
* ------------------------------------------------------------------------------
* A file that contains scripts highly couple code to the Product template.
*
* @namespace product
*/
import $ from 'jquery';
import Variants from '@shopify/theme-variants';
import {formatMoney} from '@shopify/theme-currency';
import {register} from '@shopify/theme-sections';
const selectors = {
addToCart: '[data-add-to-cart]',
addToCartText: '[data-add-to-cart-text]',
comparePrice: '[data-compare-price]',
comparePriceText: '[data-compare-text]',
originalSelectorId: '[data-product-select]',
priceWrapper: '[data-price-wrapper]',
productImageWrapper: '[data-product-image-wrapper]',
productFeaturedImage: '[data-product-featured-image]',
productJson: '[data-product-json]',
productPrice: '[data-product-price]',
productThumbs: '[data-product-single-thumbnail]',
singleOptionSelector: '[data-single-option-selector]',
bundleTemplate: '.template-pack-single',
oosForm: '.js-OOS-form',
};
// JAKI
const $bundleTemplate = $(selectors.bundleTemplate);
const $oosForm = $(selectors.oosForm);
if ($bundleTemplate.length > 0) {
console.log($bundleTemplate);
} else {
console.log('ne postoji');
}
function isAnySizeOptionSelected() {
return $('.p-single-product--bundle-single-active .c-size-picker__sizes input[type=radio]:checked').length > 0;
}
function isAnyButtonDisabled() {
return $('.js-ajax-disabled-bundle-button:disabled').length > 0;
}
console.log(isAnySizeOptionSelected());
console.log(isAnyButtonDisabled());
// JAKI
const cssClasses = {
activeThumbnail: 'active-thumbnail',
hide: 'hide',
};
const keyboardKeys = {
ENTER: 13,
};
/**
* Product section constructor. Runs on page load as well as Theme Editor
* `section:load` events.
* @param {string} container - selector for the section container DOM element
*/
register('product', {
onLoad() {
this.$container = $(this.container);
this.namespace = `.${this.id}`;
// Stop parsing if we don't have the product json script tag when loading
// section in the Theme Editor
if (!$(selectors.productJson, this.$container)
.html()) {
return;
}
this.productSingleObject = JSON.parse(
$(selectors.productJson, this.$container)
.html(),
);
const options = {
$container: this.$container,
enableHistoryState: this.$container.data('enable-history-state') || false,
singleOptionSelector: selectors.singleOptionSelector,
originalSelectorId: selectors.originalSelectorId,
product: this.productSingleObject,
};
this.settings = {};
this.variants = new Variants(options);
this.$featuredImage = $(selectors.productFeaturedImage, this.$container);
this.$container.on(
`variantChange${this.namespace}`,
this.updateAddToCartState.bind(this),
);
this.$container.on(
`variantPriceChange${this.namespace}`,
this.updateProductPrices.bind(this),
);
if (this.$featuredImage.length > 0) {
this.$container.on(
`variantImageChange${this.namespace}`,
this.updateImages.bind(this),
);
}
this.initImageSwitch();
},
initImageSwitch() {
const $productThumbs = $(selectors.productThumbs, this.$container);
if (!$productThumbs.length) {
return;
}
$productThumbs
.on('click', (evt) => {
evt.preventDefault();
const imageId = $(evt.currentTarget)
.data('thumbnail-id');
this.switchImage(imageId);
this.setActiveThumbnail(imageId);
})
.on('keyup', this.handleImageFocus.bind(this));
},
handleImageFocus(evt) {
if (evt.keyCode !== keyboardKeys.ENTER) {
return;
}
this.$featuredImage.filter(':visible')
.focus();
},
setActiveThumbnail(imageId) {
let newImageId = imageId;
// If "imageId" is not defined in the function parameter, find it by the current product image
if (typeof newImageId === 'undefined') {
newImageId = $(
`${selectors.productImageWrapper}:not('.${cssClasses.hide}')`,
)
.data('image-id');
}
const $thumbnail = $(
`${selectors.productThumbs}[data-thumbnail-id='${newImageId}']`,
);
$(selectors.productThumbs)
.removeClass(cssClasses.activeThumbnail)
.removeAttr('aria-current');
$thumbnail.addClass(cssClasses.activeThumbnail);
$thumbnail.attr('aria-current', true);
},
switchImage(imageId) {
const $newImage = $(
`${selectors.productImageWrapper}[data-image-id='${imageId}']`,
this.$container,
);
const $otherImages = $(
`${selectors.productImageWrapper}:not([data-image-id='${imageId}'])`,
this.$container,
);
$newImage.removeClass(cssClasses.hide);
$otherImages.addClass(cssClasses.hide);
},
/**
* Updates the DOM state of the add to cart button
*
* @param {boolean} enabled - Decides whether cart is enabled or disabled
* @param {string} text - Updates the text notification content of the cart
*/
updateAddToCartState(evt) {
const variant = evt.variant;
console.log(isAnySizeOptionSelected());
if ($bundleTemplate.length > 0) {
if (variant) {
$(selectors.priceWrapper, this.$container)
.removeClass(cssClasses.hide);
} else {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html(
theme.strings.unavailable,
);
$(selectors.priceWrapper, this.$container)
.addClass(cssClasses.hide);
return;
}
if (isAnySizeOptionSelected()) {
if (variant.available) {
$(selectors.addToCart, this.$container)
.prop('disabled', false);
if ($(this.$container)
.hasClass('p-single-product--bundle-single-last-step') && isAnySizeOptionSelected()) {
$(selectors.addToCartText, this.$container)
.html('Finish and add to cart');
} else {
$(selectors.addToCartText, this.$container)
.html('Proceed to next step');
}
$(this.container)
.find('.js-button')
.on('click', () => {
if ($(this.$container)
.hasClass('p-single-product--bundle-single-step-done')) {
return;
} else {
$(this.$container)
.next()
.next()
.addClass('p-single-product--bundle-single-active')
.removeClass('p-single-product--bundle-single-disabled')
.find($(selectors.addToCartText))
.html('Select your size');
$(this.$container)
.removeClass('p-single-product--bundle-single-active');
$(this.$container)
.addClass('p-single-product--bundle-single-step-done');
}
});
} else {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html(theme.strings.soldOut);
}
} else if ($(this.$container)
.hasClass('p-single-product--bundle-single-step-done')) {
if (variant.available) {
$(selectors.addToCart, this.$container)
.prop('disabled', false);
$(selectors.addToCartText, this.$container)
.html('Proceed to next step');
} else {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html(theme.strings.soldOut);
}
} else if (variant.available) {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html('Select your size');
} else {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html('Select your size');
}
} else {
if (variant) {
$(selectors.priceWrapper, this.$container)
.removeClass(cssClasses.hide);
} else {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html(
theme.strings.unavailable,
);
$(selectors.priceWrapper, this.$container)
.addClass(cssClasses.hide);
return;
}
if (variant.available) {
$(selectors.addToCart, this.$container)
.prop('disabled', false);
$(selectors.addToCartText, this.$container)
.html(theme.strings.addToCart);
$($oosForm)
.removeClass('is-visible');
} else {
$(selectors.addToCart, this.$container)
.prop('disabled', true);
$(selectors.addToCartText, this.$container)
.html(theme.strings.soldOut);
$($oosForm)
.addClass('is-visible');
}
}
},
updateImages(evt) {
const variant = evt.variant;
const imageId = variant.featured_image.id;
this.switchImage(imageId);
this.setActiveThumbnail(imageId);
},
/**
* Updates the DOM with specified prices
*
* @param {string} productPrice - The current price of the product
* @param {string} comparePrice - The original price of the product
*/
updateProductPrices(evt) {
const variant = evt.variant;
const $comparePrice = $(selectors.comparePrice, this.$container);
const $compareEls = $comparePrice.add(
selectors.comparePriceText,
this.$container,
);
$(selectors.productPrice, this.$container)
.html(
formatMoney(variant.price, theme.moneyFormat),
);
if (variant.compare_at_price > variant.price) {
$comparePrice.html(
formatMoney(variant.compare_at_price, theme.moneyFormat),
);
$compareEls.removeClass(cssClasses.hide);
} else {
$comparePrice.html('');
$compareEls.addClass(cssClasses.hide);
}
},
/**
* Event callback for Theme Editor `section:unload` event
*/
onUnload() {
this.$container.off(this.namespace);
},
});
<div data-section-id="{{ section.id }}" data-section-type="product" data-enable-history-state="true"
class="{% if forloop.first == true %}p-single-product--bundle-single-active{% endif %} {% unless forloop.first == true %}p-single-product--bundle-single-disabled{% endunless %} {% if forloop.last == true %}p-single-product--bundle-single-last-step{% endif %} js-scroll-to-{{ forloop.index }}">
<section class="p-single-product p-single-product--bundle">
<!--single product slideshow and config-->
<div class="p-single-product__wrapper">
<div class="c-product-showcase c-product-showcase--bundle js-product-showcase">
<div class="c-product-showcase__main">
<div class="o-row o-row--md-fixed">
<div class="c-product-showcase__mobile-top-info">
<div class="c-product-config c-product-config--condensed">
<div class="c-product-config__top-wrap">
<div class="c-product-config__title-wrap">
<h2 class="u-a7 u-uppercase">
{% assign pack_product_title = product.title %}
{% assign stringSplit = pack_product_title | split:'-' %}
{% for word in stringSplit %}
{% unless forloop.first == true %}
{{ word | strip }}
{% endunless %}
{% endfor %}
</h2>
</div>
</div>
</div>
</div>
</div>
{%- comment -%} data-product-showcase-id must be unique number {%- endcomment -%}
<div class="c-product-showcase__preview c-product-preview js-product-showcase-preview"
data-product-showcase-id="{{ product-loop-index }}">
{%- assign current_variant = product.selected_or_first_available_variant -%}
{%- assign featured_image = current_variant.featured_image | default: product.featured_image -%}
<div class="c-product-preview__main-images">
{% for image in product.images %}
{% capture wrapper_class %}
{%- unless featured_image == image -%}
hide
{%- endunless -%}
{% endcapture %}
{% include 'responsive-image' with
image: image,
image_class: 'c-product-preview__big-image'
max_width: 700,
max_height: 800,
image_attributes: "data-product-featured-image",
wrapper_class: wrapper_class,
wrapper_attributes: "data-product-image-wrapper", %}
{% endfor %}
</div>
{% if product.images.size > 1 %}
<div class="c-product-preview__product-list-group">
{% for image in product.images %}
{% if image.variants.size > 0 and forloop.first == true %}
{% assign current_color = image.variants[0].option1 %}
<ul class="c-product-preview__product-list js-product-preview-thumbnail-list is-current" data-color="{{ current_color | handleize }}">
{% endif %}
{% if image.variants.size > 0 and image.variants[0].option1 != current_color and forloop.last == false %}
</ul>
{% assign current_color = image.variants[0].option1 %}
<ul class="c-product-preview__product-list js-product-preview-thumbnail-list" data-color="{{ current_color | handleize }}">
{% endif %}
<li class="c-product-preview__product-list-item">
<a class="c-product-preview__product-link js-product-showcase-preview-link {% if forloop.first == true %} active-thumbnail {% endif %}"
href="{{ image.src | img_url: '480x480' }}"
data-color="{{ current_color | handleize }}"
data-variant="{{ image.variants[0].option1 }}"
data-thumbnail-id="{{ image.id }}"
data-product-single-thumbnail>
<img class="product-single__thumbnail-image c-product-preview__small-image"
src="{{ image.src | img_url: '160x160' }}"
alt="{{ image.alt | escape }}">
</a>
</li>
{% if forloop.last == true %}
</ul>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="js-product-showcase-sliders-group c-product-showcase__sliders-group"
data-product-showcase-id="{{ product-loop-index }}">
{% if product.images.size > 1 %}
{% for image in product.images %}
{% if image.variants.size > 0 and forloop.first == true %}
{% assign current_color = image.variants[0].option1 %}
<div class="c-preview-slider c-slider js-preview-slider-component is-current" data-color="{{ current_color | handleize }}">
<div class="c-preview-slider__slider-container swiper-container js-preview-slider-container">
<div class="c-slider__wrapper swiper-wrapper">
{% endif %}
{% if image.variants.size > 0 and image.variants[0].option1 != current_color and forloop.last == false %}
</div><!-- .c-slider__wrapper -->
</div><!-- .c-preview-slider__slider-container -->
<div class="c-preview-slider__bullets js-slider-bullets">
</div><!-- .c-preview-slider__bullets -->
</div><!-- .c-preview-slider -->
{% assign current_color = image.variants[0].option1 %}
<div class="c-preview-slider c-slider js-product-showcase-preview-slider js-preview-slider-component" data-color="{{ current_color | handleize }}">
<div class="c-preview-slider__slider-container swiper-container js-preview-slider-container">
<div class="c-slider__wrapper swiper-wrapper">
{% endif %}
<div class="c-slider__item swiper-slide">
<div class="c-slider__image js-slide-image"
style="background-image: url('{{ image.src | img_url: '480x480' }}')"></div>
</div><!-- .c-slider__item -->
{% if forloop.last == true %}
</div><!-- .c-slider__wrapper -->
</div><!-- .c-preview-slider__slider-container -->
<div class="c-preview-slider__bullets js-slider-bullets">
</div><!-- .c-preview-slider__bullets -->
</div><!-- .c-preview-slider -->
{% endif %}
{% endfor %}
{% endif %}
</div><!-- .js-product-showcase-sliders-group -->
</div>
<div class="c-product-showcase__side">
<div class="p-single-product__config p-single-product__config--bundle">
{% assign color = 'black' %}
{% assign mobileTitleHidden = true %}
{% assign hideReviews = true %}
{% assign firstChunk = 'data-product-showcase-id="' %}
{% assign lastChunk = '"' %}
{% assign colorPickerDataAttributes = firstChunk | append: product-loop-index | append: lastChunk %}
<div class="c-product-config {% if mobileTitleHidden %} c-product-config--mobile-title-hidden {% endif %}">
<div class="c-product-config__top-wrap">
<div class="c-product-config__title-wrap c-product-config__title-wrap--bundle">
<!--bundle info-->
<div class="c-product-config__bundle-info">
<!--bundle step-->
<div class="c-product-config__bundle-step u-b4">
step 0{{ product-loop-index }} / 0{{ product-loop-length }}
</div>
<!--end bundle step-->
<!--bundle price-->
<div class="c-product-config__bundle-price u-b4">
Clinton Pack $75
</div>
<!--end bundle price-->
</div>
<!--end bundle info-->
<h2 class="u-a7 u-uppercase">
<a href="{{ product.url | within: collection }}">
{% assign pack_product_title = product.title %}
{% assign stringSplit = pack_product_title | split:'-' %}
{% for word in stringSplit %}
{% unless forloop.first == true %}
{{ word | strip }}
{% endunless %}
{% endfor %}
</a>
</h2>
</div>
</div>
<!--product form-->
<form action="/cart/add" method="post" enctype="multipart/form-data"
class="js-ajax-bundle-form js-ajax-bundle-form-{{ product-loop-index }}"
data-product-id="{{ product.selected_or_first_available_variant.id }}">
<!--product variants-->
{% unless product.has_only_default_variant %}
{% for option in product.options_with_values %}
{% assign option-name = option.name | downcase %}
{% assign is_color = false %}
{% assign stripped-value = value | split: ' ' | last | handle %}
{% assign product-handle = product.handle %}
{% if option-name contains 'color' or option-name contains 'colour' %}
{% assign is_color = true %}
{% endif %}
{% if is_color %}
<div class="c-product-config__color-picker">
{% include "color-picker" with option-name, stripped-value, product-handle %}
</div>
{% endif %}
{% assign is_size = false %}
{% if option-name contains 'size' %}
{% assign is_size = true %}
{% endif %}
{% if is_size %}
<div class="c-product-config__size-picker">
{% include "size-picker-bundle" with option-name, stripped-value, product-handle %}
</div>
{% endif %}
{% endfor %}
{% endunless %}
<select name="id" class="no-js" data-product-select>
{% for variant in product.variants %}
<option
{% if variant == current_variant %}selected="selected"{% endif %}
{% unless variant.available %}disabled="disabled"{% endunless %}
value="{{ variant.id }}">
{{ variant.title }}
</option>
{% endfor %}
</select>
<!--end product variants-->
{% if forloop.last == true %}
<!--product add to cart-->
<button
class="button button--black button--full-width c-product-config__add-to-cart js-button js-ajax-add-bundle-to-cart"
type="submit"
name="add"
data-add-to-cart
disabled="disabled">
<span data-add-to-cart-text>
{% if current_variant.available %}
Complete previous step
{% else %}
{{ 'products.product.sold_out' | t }}
{% endif %}
</span>
</button>
<!--end product add to cart-->
<div class="c-form-errors js-ajax-bundle-error"></div>
{% elsif forloop.first == true %}
<!--product next step-->
<button
class="button button--black button--full-width c-product-config__add-to-cart js-button js-ajax-disabled-bundle-button"
type="submit"
name="add"
data-scroll-to="on"
data-scroll-to-target=".js-scroll-to-{{ forloop.index | plus:1 }}"
data-add-to-cart
disabled="disabled">
<span data-add-to-cart-text>
{% if current_variant.available %}
Select your size
{% else %}
{{ 'products.product.sold_out' | t }}
{% endif %}
</span>
</button>
<!--end product next step-->
{% else %}
<!--product next step-->
<button
class="button button--black button--full-width c-product-config__add-to-cart js-button js-ajax-disabled-bundle-button"
type="submit"
name="add"
data-scroll-to="on"
data-scroll-to-target=".js-scroll-to-{{ forloop.index | plus:1 }}"
data-add-to-cart
disabled="disabled">
<span data-add-to-cart-text>
{% if current_variant.available %}
Complete previous step
{% else %}
{{ 'products.product.sold_out' | t }}
{% endif %}
</span>
</button>
<!--end product next step-->
{% endif %}
</form>
<!--end product form-->
<p class="c-product-config__additional-info u-b4">
Free shipping on all domestic orders
</p>
</div>
</div>
</div>
</div><!-- .c-product-showcase -->
</div>
<!--end single product slideshow and config-->
{% unless product == empty %}
<script type="application/json" data-product-json>{{ product | json }}</script>
{% endunless %}
</section>
</div>
<script type="application/ld+json">
{
"@context": "http://schema.org/",
"@type": "Product",
"name": "{{ product.title | escape }}",
"url": "{{ shop.url }}{{ product.url }}",
{% if product.featured_image %}
{% assign image_size = product.featured_image.width | append: 'x' %}
"image": [
"https:{{ product.featured_image.src | img_url: image_size }}"
],
{% endif %}
"description": "{{ product.description | strip_html | escape }}",
{% if current_variant.sku != blank %}
"sku": "{{ current_variant.sku }}",
{% endif %}
"brand": {
"@type": "Thing",
"name": "{{ product.vendor | escape }}"
},
{% if product.variants %}
"offers": [
{% for variant in product.variants %}
{
"@type" : "Offer",
"availability" : "http://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}",
"price" : "{{ variant.price | divided_by: 100.00 }}",
"priceCurrency" : "{{ shop.currency }}",
"url" : "{{ shop.url }}{{ variant.url }}",
"itemOffered" :
{
"@type" : "Product",
{% if variant.image %}
{% assign variant_image_size = variant.image.width | append: 'x' %}
"image": "http:{{ variant.image.src | img_url: variant_image_size }}",
{% endif %}
{% if variant.title != blank %}
"name" : "{{ variant.title | escape }}",
{% endif %}
{% if variant.sku != blank %}
"sku": "{{ variant.sku }}",
{% endif %}
{% if variant.weight != blank %}
"weight": {
"@type": "QuantitativeValue",
{% if variant.weight_unit != blank %}
"unitCode": "{{ variant.weight_unit }}",
{% endif %}
"value": "{{ variant.weight | weight_with_unit: variant.weight_unit }}"
},
{% endif %}
"url": "{{ shop.url }}{{ variant.url }}"
}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
{% endif %}
}
</script>
<div class="c-size-picker js-size-picker {% if color == 'white' %} c-size-picker--white {% endif %}">
<div class="c-size-picker__sizes u-b2">
{% for value in option.values %}
<input type="radio" name="{{ option-name }}-{{ product-handle }}"
class="c-size-picker__radio-btn js-size-picker-radio"
data-single-option-selector
data-index="option{{ option.position }}"
value="{{ value | escape }}"
id="variant_{{ option-name }}-{{ product-handle }}-{{ forloop.index0 }}"/>
<label for="variant_{{ option-name }}-{{ product-handle }}-{{ forloop.index0 }}"
class="c-size-picker__radio-label u-align-center">
{{ value }}
</label>
{% endfor %}
</div>
<div class="c-size-picker__calculate-wrap">
<span class="c-size-picker__calculate u-b5 js-modal-trigger">Calculate Size</span>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment