Варианты товара Shopify Slate не обновляются в корзине - PullRequest
1 голос
/ 23 мая 2019

Варианты товара (выбранные с помощью образцов) не обновляются при отправке в корзину - он просто выбирает исходный вариант, выбранный при загрузке страницы. Пробовал решения от (если я что-то полностью пропустил):

https://github.com/Shopify/slate/issues/116 https://github.com/Shopify/starter-theme/issues/127 https://github.com/Shopify/starter-theme/blob/master/src/scripts/sections/product.js#L71

product.liquid

<div data-section-id="{{ section.id }}" data-section-type="product" data-enable-history-state="true">
  <form action="/cart/add" method="post" enctype="multipart/form-data">

    {%- assign current_variant = product.selected_or_first_available_variant -%}
    {%- assign featured_image = current_variant.featured_image | default: product.featured_image -%}
    {% capture productTitle %}{% include 'product-title' %}{% endcapture %}
    {% capture productPrice %}{% include 'product-price' %}{% endcapture %}
    {%- capture image_alt_text -%}
      {{ productTitle }} - {{ shop.name }}
    {%- endcapture -%}

    {% if product.images.size > 1 %}
      <ul class="productThumbs">
        {% for image in product.images %}
          <li>
            <a href="{{ image.src | img_url: '480x480' }}" data-thumbnail-id="{{ image.id }}" data-product-single-thumbnail>
              <img class="product-single__thumbnail-image" src="{{ image.src | img_url: '50x' }}" alt="{{ image_alt_text }}">
            </a>
          </li>
        {% endfor %}
      </ul>
    {% endif %}

    <div class="mobileOnly">
      <div class="vendor">{{ product.vendor }}</div>
      <h1 class="title">{{ productTitle }}</h1>
    </div>


    <div class="mainImage">
      {% assign counter = 0 %}

      {% for image in product.images %}
        {% capture wrapper_class %}
          {%- unless featured_image == image -%}
            hide
          {%- endunless -%}
        {% endcapture %}

        {% capture image_attributes %}
          data-product-featured-image data-large="{{ image | img_url: '2048x2048' }}"
        {% endcapture %}

        {% capture full_resolution_image %}
          {{ image.src | img_url: 'master' }}
        {% endcapture %}

        <div class="{{ wrapper_class }} imageArea">
          {% include 'responsive-image' with
            image: image,
            max_width: 2048,
            max_height: 2048,
            image_attributes: image_attributes,
            image_class: 'cloudzoom',
            wrapper_class: wrapper_class,
            wrapper_attributes: "data-product-image-wrapper",
            image_alt: image_alt_text,
            data_cloudzoom_image: full_resolution_image
          %}

        </div>

        {% assign counter = counter | plus: 1 %}

      {% endfor %}

      <div class="imageInstructions" id="hoverToZoom">Roll over image to zoom in</div>
      <div class="imageInstructions visually-hidden" id="clickToExpand">Click image to open expanded view</div>
    </div>    

    <div class="quickInfo">

      <h1 class="title desktopOnly">{{ productTitle }}</h1>

      <div class="vendor desktopOnly">by {{ product.vendor }}</div>

      {{ productPrice }}

      <div class="rte">
        <div class="description desktopOnly">
          {{ product.description }}
        </div>

        {% include 'product-variants' %}
      </div>

    </div>

    <div class="actionsBoxContainer">

      {% if section.settings.show_share_buttons %}
        <div class="socialSharingBox desktopOnly">
          <span class="shareButtonInstructions">Share</span>
          {% include 'social-sharing', share_title: productTitle, share_permalink: product.url, share_image: product %}
        </div>
      {% endif %}

      <div class="actionsBox">

        {{ productPrice }}

        {% if product.available %}
          <div class="stock inStock">In Stock.</div>
        {% else %}
          <div class="stock noStock">Out of Stock.</div>
        {% endif %}

        <div class="shipsFrom desktopOnly">
          Ships from and sold by {{ shop.name }}.
        </div>

        <div class="quantity visually-hidden">
          <label for="Quantity">{{ 'products.product.quantity' | t }}</label>
          <input type="number" id="Quantity" name="quantity" value="1" min="1">
        </div>

        <button
          type="submit"
          name="add"
          class="button"
          data-add-to-cart
          {% unless current_variant.available %}disabled="disabled"{% endunless %}
          {% unless current_variant.price != 0 %}disabled="disabled"{% endunless %}>
            <span data-add-to-cart-text>
              {% if current_variant.available %}
                <span class="icon">{% include 'icon-cart' %}</span>
                <span class="text">{{ 'products.product.add_to_cart' | t }}</span>
              {% else %}
                <span class="soldOut">{{ 'products.product.sold_out' | t }}</span>
              {% endif %}
            </span>
        </button>

        <div class="shipsFrom mobileOnly">
          Ships from and sold by {{ shop.name }}.
        </div>

        {% include 'deliver-to' %}

      </div>

      <h2 class="about mobileOnly">About this item</h2>

      <div class="moreInfo mobileOnly">

        <div class="description">{{ product.description }}</div>

        {% include 'product-variants' %}
      </div>

      {% if section.settings.show_share_buttons %}
        <div class="socialSharingBox mobileOnly">
          <span class="shareButtonInstructions">Share</span>
          {% include 'social-sharing', share_title: productTitle, share_permalink: product.url, share_image: product %}
        </div>
      {% endif %}

    {% unless product == empty %}
      <script type="application/json" data-product-json>
        {{ product | json }}
      </script>
    {% endunless %}

  </form>

</div>

{% schema %}
  {
    "name": "Product pages",
    "settings": [
      {
        "type": "checkbox",
        "id": "show_share_buttons",
        "label": "Show social sharing buttons",
        "default": true
      }
    ]
  }
{% endschema %}

{% javascript %}

  $( document ).ready(function() {

    $('ul.productThumbs li').on( 'click', function() {

      var i = $(this).index();

      $('.mainImage > div.imageArea').addClass('hide');
      $('.mainImage > div.imageArea').eq(i).removeClass('hide');

    });

    $('img.cloudzoom').on('cloudzoom_start_zoom', function() {
      $('#clickToExpand').removeClass('visually-hidden');
      $('#hoverToZoom').addClass('visually-hidden');
    });

    $('img.cloudzoom').on('cloudzoom_end_zoom', function() {
      $('#hoverToZoom').removeClass('visually-hidden');
      $('#clickToExpand').addClass('visually-hidden');
    });

    var productGalleryItems = [];

    $('.mainImage img').each(function() {
      const cloudZoomData = $(this).data('cloudzoom') + '';
      const cloudZoomDataImage = cloudZoomData.split(',')[0];
      const fullSizeImgSrc = cloudZoomDataImage.split("zoomImage: '")[1].slice(0,-1);
      const thumbImgParts = fullSizeImgSrc.split('/products/');
      const thumbImgUrlBase = thumbImgParts[0];
      const thumbImgEnd = thumbImgParts[1];
      const thumbImgEndParts = thumbImgEnd.split('?v=');
      const thumbImgFileExtension = '.' + thumbImgEndParts[0].split('.').pop();
      const thumbImgFileName = thumbImgEndParts[0].split(thumbImgFileExtension)[0];
      const thumbImgVersion = "?v=" + thumbImgEndParts[1];
      const thumbImgSrc = "'" + thumbImgUrlBase + "/products/" + thumbImgFileName + "_medium" + thumbImgFileExtension + thumbImgVersion + "'";


      const galleryItemObject = {
        src: fullSizeImgSrc,
        opts: {
          thumb: thumbImgSrc,
        }
      }

      productGalleryItems.push(galleryItemObject);

    });

    $('img.cloudzoom').on('click',function(){
        var cloudZoom = $(this).data('CloudZoom');
        cloudZoom.closeZoom();
        $.fancybox.open(productGalleryItems);
        return false;
    });

  });

{% endjavascript %}

product.js

/**
 * 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]',
};

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),
      );
    }

    $( ".swatch input.swatch-element" ).click(function() {

      // console.log(this);

      // $(".swatch input.swatch-element").each(function() {
      //   $(this).prop('checked', false);  
      // });

      // var checkbox = ".swatch input#" + this.id;

      // $(checkbox).prop('checked', !$(checkbox).prop('checked'));

      $(".swatch label.selectedOnLoad").each(function() { 
        $(this).removeClass("selectedOnLoad");
      });
    });

    this.highlightSelectedVariantOnLoad();
    this.initImageSwitch();
  },

  highlightSelectedVariantOnLoad() {
    $(".swatch input.swatch-element:checked").each(function() { 
      $(".swatch label[for='" + this.id + "']").addClass("selectedOnLoad");  
    });
  },

  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;

    if (variant) {
      $(selectors.priceWrapper, this.$container).removeClass(cssClasses.hide);
    } else {
      $(selectors.addToCart, this.$container).prop('disabled', true);
      $(selectors.priceWrapper, this.$container).addClass(cssClasses.hide);
      return;
    }

    if (variant.available) {
      $(selectors.addToCart, this.$container).prop('disabled', false);
    } else {
      $(selectors.addToCart, this.$container).prop('disabled', true);
    }

    if (variant.price != 0) {
      $(selectors.addToCart, this.$container).prop('disabled', false);
    } else {
      $(selectors.addToCart, this.$container).prop('disabled', true);
    }
  },

  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,
    );

    const freeText = $('.freeText').html();

    if (variant.price == 0) {
      $(selectors.productPrice, this.$container).html(
        freeText,
      );
    }  else {
      $(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);
  },
});
...