Cores no item do produto

Neste tutorial, vamos ver como visualizar as cores de um produto na listagem, sem precisar inserir nos detalhes do produto.

HTML

1. A primeira coisa que vamos fazer é adicionar um novo snipplet chamado item-colors.tpl dentro da pasta snipplets/grid com o seguinte código:

{% if product.variations %}
    {% set own_color_variants = 0 %}
    {% set custom_color_variants = 0 %}


    {% for variation in product.variations %}
        <div class="js-color-variant-available-{{ loop.index }} {% if variation.name in ['Color', 'Cor'] %}js-color-variant-active{% endif %}" data-value="variation_{{ loop.index }}" data-option="{{ loop.index0 }}" >
            {% if variation.name in ['Color', 'Cor'] %}
                {% if variation.options | length > 1 %}
                    <div class="item-colors">
                        <a href="{{ product_url_with_selected_variant }}" class="item-colors-bullet item-colors-bullet-text d-md-none w-auto px-2">{{ variation.options | length }} {{ 'colores' | translate }}</a>
                        <div class="d-none d-md-block">
                            {% for option in variation.options | take(5) if option.custom_data %}
                                <span title="{{ option.name }}" data-option="{{ option.id }}" class="js-color-variant item-colors-bullet {% if product.default_options[variation.id] == option.id %}selected{% endif %}" style="background: {{ option.custom_data }}"></span>
                            {% endfor %}


                            {% for option in variation.options %}
                                {% if option.custom_data %}
                                    {# Quantity of our colors #}
                                    {% set own_color_variants = own_color_variants + 1 %}
                                {% else %}
                                    {# Quantity of custom colors #}
                                    {% set custom_color_variants = custom_color_variants + 1 %}
                                {% endif %}
                            {% endfor %}


                            {% set more_color_variants = (own_color_variants - 5) + custom_color_variants %}


                            {% if own_color_variants and custom_color_variants %}
                                <a href="{{ product_url_with_selected_variant }}" class="item-colors-bullet w-auto" title="{{ 'Ver más colores' | translate }}">
                                    {% if own_color_variants > 5 %}
                                        +{{ more_color_variants }}
                                    {% else %}
                                        +{{ custom_color_variants }}
                                    {% endif %}
                                </a>
                            {% elseif own_color_variants > 5 %}
                                <a href="{{ product_url_with_selected_variant }}" class="item-colors-bullet w-auto" title="{{ 'Ver más colores' | translate }}">+{{ own_color_variants - 5 }}</a>
                            {% elseif custom_color_variants %}
                                <a href="{{ product_url_with_selected_variant }}" class="item-colors-bullet item-colors-bullet-text w-auto px-2" title="{{ 'Ver más colores' | translate }}">{{ custom_color_variants }} {{ 'colores' | translate }}</a>
                            {% endif %}
                        </div>
                    </div>
                {% endif %}
            {% endif %}
        </div>
    {% endfor %}
{% endif %}

2. Depois vamos procurar o snipplet item.tpl dentro da pasta snipplets/grid, pode ser que em seu layout esse snipplet seja chamado single_product.tpl, e usamos o seguinte código:

{% set slide_item = slide_item | default(false) %}
{% set columns = settings.grid_columns %}
{% set has_color_variant = false %}
{% if settings.product_color_variants %}
    {% for variation in product.variations if variation.name in ['Color', 'Cor'] and variation.options | length > 1 %}
        {% set has_color_variant = true %}
    {% endfor %}
{% endif %}


<div class="js-item-product {% if slide_item %}swiper-slide{% else %}col{% if columns == 2 %}-6 col-md-3{% else %}-12 col-md-4{% endif %}{% endif %} item item-product{% if not product.display_price %} no-price{% endif %}" data-product-type="list" data-product-id="{{ product.id }}" data-store="product-item-{{ product.id }}">


    {% if settings.product_color_variants %}
        <div id="quick{{ product.id }}{% if slide_item and section_name %}-{{ section_name }}{% endif %}" class="js-product-container js-quickshop-container {% if product.variations %}js-quickshop-has-variants{% endif %}" data-variants="{{ product.variants_object | json_encode }}">
    {% endif %}


        {% set product_url_with_selected_variant = has_filters ?  ( product.url | add_param('variant', product.selected_or_first_available_variant.id)) : product.url  %}


        {% if has_color_variant %}


            {# Item image will be the first avaiable variant #}


            {% set item_img_spacing = product.featured_variant_image.dimensions['height'] / product.featured_variant_image.dimensions['width'] * 100 %}
            {% set item_img_srcset = product.featured_variant_image %}
            {% set item_img_alt = product.featured_variant_image.alt %}
        {% else %}


            {# Item image will be the first image regardless the variant #}


            {% set item_img_spacing = product.featured_image.dimensions['height'] / product.featured_image.dimensions['width'] * 100 %}
            {% set item_img_srcset = product.featured_image %}
            {% set item_img_alt = product.featured_image.alt %}
        {% endif %}


        <div class="item-image mb-2">
            <div style="padding-bottom: {{ item_img_spacing }}%;" class="p-relative" data-store="product-item-image-{{ product.id }}">
                <a href="{{ product_url_with_selected_variant }}" title="{{ product.name }}">
                    <img alt="{{ item_img_alt }}" data-sizes="auto" data-expand="-10" src="{{ 'images/empty-placeholder.png' | static_url }}" data-srcset="{{ item_img_srcset | product_image_url('small')}} 240w, {{ item_img_srcset | product_image_url('medium')}} 320w, {{ item_img_srcset | product_image_url('large')}} 480w" class="lazyautosizes lazyload img-absolute img-absolute-centered fade-in" /> 
                    <div class="placeholder-fade"></div>
                </a>
                {% if settings.product_color_variants %}
                    {% include 'snipplets/labels.tpl' with {color: true} %}
                    {% include 'snipplets/grid/item-colors.tpl' %}
                {% else %}
                    {% include 'snipplets/labels.tpl' %}
                {% endif %}
            </div>
        </div>
        {% if settings.product_color_variants and product.variations %}


            {% for variation in product.variations if variation.name in ['Color', 'Cor'] and variation.options | length > 1 %}


                {# Hidden product form to update item image and variants #}
                
                <div class="js-item-variants hidden">
                    <form id="product_form" class="js-product-form" method="post" action="{{ store.cart_url }}">
                        {% if product.variations %}
                            {% include "snipplets/product/product-variants.tpl" with {quickshop: true} %}
                        {% endif %}
                        {% set state = store.is_catalog ? 'catalog' : (product.available ? product.display_price ? 'cart' : 'contact' : 'nostock') %}
                        {% set texts = {'cart': "Agregar al carrito", 'contact': "Consultar precio", 'nostock': "Sin stock", 'catalog': "Consultar"} %}


                        {# Add to cart CTA #}


                        <input type="submit" class="js-addtocart js-prod-submit-form {{ state }}" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} />
                    </form>
                </div>


            {% endfor %}
        {% endif %}
        <div class="item-description" data-store="product-item-info-{{ product.id }}">
            <a href="{{ product_url_with_selected_variant }}" title="{{ product.name }}" class="item-link">
                <div class="item-name mb-1" data-store="product-item-name-{{ product.id }}">{{ product.name }}</div>
                {% if product.display_price %}
                    <div class="item-price-container mb-1" data-store="product-item-price-{{ product.id }}">
                        <span class="js-compare-price-display price-compare" {% if not product.compare_at_price or not product.display_price %}style="display:none;"{% else %}style="display:inline-block;"{% endif %}>
                            {{ product.compare_at_price | money }}
                        </span>
                        <span class="js-price-display item-price">
                            {{ product.price | money }}
                        </span>


                    </div>
                {% endif %}
            </a>
        </div>
        {% include 'snipplets/payments/installments.tpl' %}


        {# Structured data to provide information for Google about the product content #}
        {% include 'snipplets/structured_data/item-structured-data.tpl' %}
    {% if settings.product_color_variants %}
        </div>
    {% endif %}
</div>

3. No snipplet labels.tpl, pode ser que em seu layout esteja diretamente dentro do snipplet single_product.tpl, utilizamos o seguinte código:

{% if product.compare_at_price > product.price %}
{% set price_discount_percentage = ((product.compare_at_price) - (product.price)) * 100 / (product.compare_at_price) %}
{% endif %}


{% if color %}
  {% set show_labels = settings.product_color_variants %}
{% else %}
  {% set show_labels = not product.has_stock or product.free_shipping or product.compare_at_price or product.promotional_offer %}
{% endif %}


{% if show_labels %}
  <div class="labels">
    {% if not product.has_stock %}
      <div class="{% if product_detail %}js-stock-label {% endif %}label label-default">{{ "Sin stock" | translate }}</div>
    {% else %}
      {% if product_detail or color %}
        <div class="js-stock-label label label-default" {% if product.has_stock %}style="display:none;"{% endif %}>{{ "Sin stock" | translate }}</div>
      {% endif %}
      {% if product.compare_at_price or product.promotional_offer %}
        <div class="{% if not product.promotional_offer and product %}js-offer-label{% endif %} label label-primary" {% if (not product.compare_at_price and not product.promotional_offer) or not product.display_price %}style="display:none;"{% endif %}>
          {% if product.promotional_offer.script.is_percentage_off %}
            {{ product.promotional_offer.parameters.percent * 100 }}% OFF
          {% elseif product.promotional_offer.script.is_discount_for_quantity %}
            <div>{{ product.promotional_offer.selected_threshold.discount_decimal_percentage * 100 }}% OFF</div>
            <div class="label-small p-right-quarter p-left-quarter">{{ "Comprando {1} o más" | translate(product.promotional_offer.selected_threshold.quantity) }}</div>
          {% elseif product.promotional_offer %}
            {% if store.country == 'BR' %}
              {{ "Leve {1} Pague {2}" | translate(product.promotional_offer.script.quantity_to_take, product.promotional_offer.script.quantity_to_pay) }}
            {% else %}
              {{ "Promo" | translate }} {{ product.promotional_offer.script.type }} 
            {% endif %}
          {% else %}
            <span {% if product_detail or color %}class="js-offer-percentage"{% endif %}>{{ price_discount_percentage |round }}</span>% OFF
          {% endif %}
        </div>
      {% endif %}
      {% if product.free_shipping %}
        <div class="label label-secondary">{{ "Envío gratis" | translate }}</div>
      {% endif %}
    {% endif %}
  </div>
{% endif %}

4. No snipplet installmets.tpl dentro da pasta snipplets/payments, para atualizar as informações de parcelas ao alterar uma cor, substituímos esta linha de código:

<div class="{% if product_detail %}js-max-installments-container js-max-installments text-center text-md-left{% else %}item-installments{% endif %}">

Pela seguinte:

<div class="js-max-installments-container js-max-installments {% if product_detail %}text-center text-md-left{% else %}item-installments{% endif %}">

5. Por úlitmo, no snipplet product-variants.tpl dentro da pasta snipplets/product, usamos:

<div class="js-product-variants{% if quickshop %} js-product-quickshop-variants{% endif %} form-row">
    {% for variation in product.variations %}
        <div class="js-product-variants-group {% if variation.name in ['Color', 'Cor'] %}js-color-variants-container{% endif %} {% if loop.length == 3 %} col-12 col-md-4 {% elseif loop.length == 2 %} col-6 {% else %} col col-md-6{% endif %}">
            {% embed "snipplets/forms/form-select.tpl" with{select_label: true, select_label_name: '' ~ variation.name ~ '', select_for: 'variation_' ~ loop.index , select_id: 'variation_' ~ loop.index, select_name: 'variation' ~ '[' ~ variation.id ~ ']', select_custom_class: 'js-variation-option js-refresh-installment-data'} %}
                {% block select_options %}
                    {% for option in variation.options %}
                        <option value="{{ option.id }}" {% if product.default_options[variation.id] == option.id %}selected="selected"{% endif %}>{{ option.name }}</option>
                    {% endfor %}
                {% endblock select_options%}
            {% endembed %}
        </div>
    {% endfor %}
</div>

CSS

Requisito:

Ter adicionado helper classes em seu layout. Você pode seguir este pequeno tutorial para fazer isso (é só copiar e colar algumas classes, não leva mais que 1 minuto).

1.Adicione os estilos no arquivo static/style-critical.tpl

Se em seu layout você usar um stylesheet para o CSS crítico, precisaremos do seguinte código dentro dele.

.item-colors {
  position: absolute;
  bottom: 0;
  z-index: 9;
  width: 100%;
  padding: 5px 0;
}
.item-colors-bullet {
  display: inline-block;
  min-width: 18px;
  height: 18px;
  margin: 0 3px;
  font-size: 10px;
  text-transform: uppercase;
  line-height: 19px;
  vertical-align: top;
  border-radius: 18px;
  cursor: pointer;
  opacity: 0.8;
  -webkit-transition: all 0.4s ease;
  -ms-transition: all 0.4s ease;
  -moz-transition: all 0.4s ease;
  -o-transition: all 0.4s ease;
  transition: all 0.4s ease;
}
.item-colors-bullet:hover,
.item-colors-bullet.selected {
  opacity: 1;
}

2. Adicionamos o seguinte SASS de cores em style-colors.scss.tpl (ou stylesheet do seu layout que possui as cores e fontes da loja). Lembre-se de que as variáveis de cores e fontes podem variar em relação ao seu layout:

.item-colors {
  background: rgba($main-foreground, .6);
  &-bullet {
    color: $main-foreground;
  }
  &-bullet-text {
    color: $main-background;
  }
}

JS

1. O JavaScript deve ser adicionado no arquivo store.js.tpl (ou onde você tem suas funções JS). O código de que precisamos é o seguinte:

{% if settings.product_color_variants %}


    {# Product color variations #}


    $(document).on("click", ".js-color-variant", function(e) {
        e.preventDefault();
        $this = $(this);


        var option_id = $this.data('option');
        $selected_option = $this.closest('.js-item-product').find('.js-variation-option option').filter(function() {
            return this.value == option_id;
        });
        $selected_option.prop('selected', true).trigger('change');
        var available_variant = $(this).closest(".js-quickshop-container").data('variants');


        var available_variant_color = $(this).closest('.js-color-variant-active').data('option');


        for (var variant in available_variant) {
            if (option_id == available_variant[variant]['option'+ available_variant_color ]) {


                if (available_variant[variant]['stock'] == null || available_variant[variant]['stock'] > 0 ) {


                    var otherOptions = getOtherOptionNumbers(available_variant_color);


                    var otherOption = available_variant[variant]['option' + otherOptions[0]];
                    var anotherOption = available_variant[variant]['option' + otherOptions[1]];


                    changeSelect($(this), otherOption, otherOptions[0]);
                    changeSelect($(this), anotherOption, otherOptions[1]);
                    break;


                }
            }
        }
        $this.siblings().removeClass("selected");
        $this.addClass("selected");
    });


    function getOtherOptionNumbers(selectedOption) {
        switch (selectedOption) {
            case 0:
                return [1, 2];
            case 1:
                return [0, 2];
            case 2:
                return [0, 1];
        }
    }


    function changeSelect(element, optionToSelect, optionIndex) {
        if (optionToSelect != null) {
            var selected_option_attribute = element.closest('.js-item-product').find('.js-color-variant-available-' + (optionIndex + 1)).data('value');
            var selected_option = element.closest('.js-item-product').find('#' + selected_option_attribute + " option").filter(function() {
                return this.value == optionToSelect;
            });


            selected_option.prop('selected', true).trigger('change');
        }
    }


    {# Product quickshop for color variations #}


    LS.registerOnChangeVariant(function(variant){
        {# Show product image on color change #}
        var current_image = $('img', '.js-item-product[data-product-id="'+variant.product_id+'"]');
        current_image.attr('srcset', variant.image_url);
    });


{% endif %}

2. Também precisamos adicionar o JS que atualiza os preços, estoque e rótulos correspondentes, como estoque e descontos, ao alterar as variantes.

Para isso, devemos procurar a seguinte função:

$(document).on("change", ".js-variation-option", function(e) {


    LS.changeVariant(changeVariant, '#single-product');

    (...)


});

E substituí-la pela seguinte:

$(document).on("change", ".js-variation-option", function(e) {


    var $parent = $(this).closest(".js-product-variants");
    var $variants_group = $(this).closest(".js-product-variants-group");
    var quick_id = $(this).closest(".js-quickshop-container").attr("id");
    if($parent.hasClass("js-product-quickshop-variants")){
        {% if template == 'home' %}
            LS.changeVariant(changeVariant, '.js-swiper-slide-visible #' + quick_id);
        {% else %}
            LS.changeVariant(changeVariant, '#' + quick_id);
        {% endif %}


        {% if settings.product_color_variants %}
            {# Match selected color variant with selected quickshop variant #}


            if(($variants_group).hasClass("js-color-variants-container")){
                var selected_option_id = $(this).find("option:selected").val();
                $('#' + quick_id).find('.js-color-variant').removeClass("selected");
                $('#' + quick_id).find('.js-color-variant[data-option="'+selected_option_id+'"]').addClass("selected");
            }
        {% endif %} 
    } else {
        LS.changeVariant(changeVariant, '#single-product');
    }


    (...)


});

Configurações

No arquivo config/settings.txt, adicionaremos um checkbox que ativa a funcionalidade na seção "Lista de produtos".

    title
        title = Variantes de color
    checkbox
        name = product_color_variants
        description = Mostrar variantes de color en listado de productos

Traduções

Nesta etapa, adicionamos os textos para as traduções no arquivo config/translations.txt.

es "colores"
pt "cores"
en "colors"
es_mx "colores"

es "Ver más colores"
pt "Ver mais cores"
en "See more colors"
es_mx "Ver más colores"

es "Variantes de color"
pt "Variações de cor"
en "Color variants"
es_mx "Variantes de color"

es "Mostrar variantes de color en listado de productos"
pt "Mostrar variações de cores na lista de produtos"
en "Show color variants in product list"
es_mx "Mostrar variantes de color en listado de productos"

Ativação

Por último, você pode ativar funcionalidade no Administrador Nuvem, na seção ‘Personalizar seu layout atual’ dentro de ‘Lista de produtos’:

Lembre-se, para que o produto funcione, ele deve ter pelo menos 2 opções da variante "Cor".