Neste tutorial veremos como adicionar um pop-up com o formulário do produto, que pode ser visto no item da lista de produtos.

HTML
1. A primeira coisa que vamos fazer é criar um novo snipplet chamado quick-shop.tpl dentro da pasta snipplets/grid com o seguinte código:
{% if settings.quick_shop %}
{% embed "snipplets/modal.tpl" with{modal_id: 'quickshop-modal', modal_class: 'quickshop text-center', modal_position: 'bottom modal-bottom-sheet', modal_transition: 'slide', modal_header: true, modal_footer: true, modal_width: 'centered modal-docked-md modal-docked-md-centered', modal_mobile_full_screen: 'true' } %}
{% block modal_body %}
<div class="js-item-product" data-product-id="">
<div class="js-product-container js-quickshop-container js-quickshop-modal js-quickshop-modal-shell" data-variants="" data-quickshop-id="">
<div class="js-item-variants">
<div class="js-item-name h1 mb-1" data-store="product-item-name-{{ product.id }}"></div>
<div class="item-price-container mb-4" data-store="product-item-price-{{ product.id }}">
<span class="js-compare-price-display h4 price-compare"></span>
<span class="js-price-display h4"></span>
</div>
{# Image is hidden but present so it can be used on cart notification #}
<img srcset="" class="js-item-image js-quickshop-img hidden"/>
<div id="quickshop-form"></div>
</div>
</div>
</div>
{% endblock %}
{% endembed %}
{% 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 %}js-item-slide 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.quick_shop or settings.product_color_variants %}
<div class="js-product-container js-quickshop-container {% if product.variations %}js-quickshop-has-variants{% endif %}" data-variants="{{ product.variants_object | json_encode }}" data-quickshop-id="quick{{ product.id }}{% if slide_item and section_name %}-{{ section_name }}{% endif %}">
{% 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="js-item-image 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.quick_shop or settings.product_color_variants) and product.variations %}
{# Hidden product form to update item image and variants: Also this is used for quickshop popup #}
<div class="js-item-variants hidden">
<form id="product_form" class="js-product-form" method="post" action="{{ store.cart_url }}">
<input type="hidden" name="add_to_cart" value="{{product.id}}" />
{% if product.variations %}
{% include "snipplets/product/product-variants.tpl" with {quickshop: true} %}
{% endif %}
{% if product.available and product.display_price and settings.quick_shop %}
{% include "snipplets/product/product-quantity.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 btn btn-primary btn-block {{ state }}" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} />
{# Fake add to cart CTA visible during add to cart event #}
{% include 'snipplets/placeholders/button-placeholder.tpl' with {custom_class: "btn-block"} %}
</form>
</div>
{% 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="js-item-name 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' %}
{% if settings.quick_shop and product.available and product.display_price %}
{# Trigger quickshop actions #}
<div class="item-actions mt-2">
{% if product.variations %}
{# Open quickshop popup if has variants #}
<a data-toggle="#quickshop-modal" data-modal-url="modal-fullscreen-quickshop" class="js-quickshop-modal-open {% if slide_item %}js-quickshop-slide{% endif %} js-modal-open js-fullscreen-modal-open btn btn-primary btn-small px-4" title="{{ 'Compra rápida de' | translate }} {{ product.name }}" aria-label="{{ 'Compra rápida de' | translate }} {{ product.name }}" >{{ 'Agregar al carrito' | translate }}</a>
{% else %}
{# If not variants add directly to cart #}
<form id="product_form" class="js-product-form" method="post" action="{{ store.cart_url }}">
<input type="hidden" name="add_to_cart" value="{{product.id}}" />
{% 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"} %}
<input type="number" name="quantity" value="1" class="js-quantity-input hidden" aria-label="{{ 'Cambiar cantidad' | translate }}" >
<input type="submit" class="js-addtocart js-prod-submit-form btn btn-primary btn-small {{ state }} px-4 mb-1" value="{{ texts[state] | translate }}" {% if state == 'nostock' %}disabled{% endif %} />
{# Fake add to cart CTA visible during add to cart event #}
{% include 'snipplets/placeholders/button-placeholder.tpl' with {custom_class: "js-addtocart-placeholder-inline btn-small mb-1"} %}
</form>
{% endif %}
</div>
{% endif %}
{# Structured data to provide information for Google about the product content #}
{% include 'snipplets/structured_data/item-structured-data.tpl' %}
{% if settings.quick_shop or settings.product_color_variants %}
</div>
{% endif %}
</div>3. Vamos adicionar um novo snipplet chamado button-placeholder.tpl dentro da pasta snipplets/placeholders para incluir transições do botão ao "Comprar". O código é o seguinte:
<div class="js-addtocart js-addtocart-placeholder btn btn-primary btn-transition disabled {{ custom_class }}" style="display: none;">
<span class="js-addtocart-text transition-container btn-transition-start active">
{{ 'Agregar al carrito' | translate }}
</span>
<span class="js-addtocart-success transition-container btn-transition-success">
{{ '¡Listo!' | translate }}
</span>
<div class="js-addtocart-adding transition-container btn-transition-progress">
{{ 'Agregando...' | translate }}
</div>
</div>4. Dentro da pasta snipplets/product, vamos editar 2 arquivos. Por um lado, o snipplet product-quantity.tpl com o seguinte código.
{% if not quickshop %}
<div class="row">
<div class="col col-md-4">
{% endif %}
{% embed "snipplets/forms/form-input.tpl" with{type_number: true, input_value: '1', input_name: 'quantity' ~ item.id, input_custom_class: 'js-quantity-input text-center', input_label: false, input_append_content: true, input_group_custom_class: 'js-quantity form-row align-items-center', form_control_container_custom_class: 'col-6', input_min: '1', input_aria_label: 'Cambiar cantidad' | translate } %}
{% block input_prepend_content %}
<span class="js-quantity-down col-3 text-center">
{% include "snipplets/svg/minus.tpl" with {svg_custom_class: "icon-inline icon-w-12 icon-lg svg-icon-text"} %}
</span>
{% endblock input_prepend_content %}
{% block input_append_content %}
<span class="js-quantity-up col-3 text-center">
{% include "snipplets/svg/plus.tpl" with {svg_custom_class: "icon-inline icon-w-12 icon-lg svg-icon-text"} %}
</span>
{% endblock input_append_content %}
{% endembed %}
{% if not quickshop %}
</div>
</div>
{% endif %}E por outro, o product-variants.tpl com o seguinte código.
<div class="js-product-variants{% if quickshop %} js-product-quickshop-variants text-left{% endif %} form-row">
{% for variation in product.variations %}
<div class="js-product-variants-group {% if loop.length == 3 %} {% if quickshop %}col-4{% else %}col-12{% endif %} col-md-4 {% elseif loop.length == 2 %} col-6 {% else %} col {% if quickshop %}col-md-12{% else %}col-md-6{% endif %}{% endif %}" data-variation-id="{{ variation.id }}">
{% 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_data_value: '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>5. Adicionamos um parâmetro de select_data no snipplet form-select.tpl dentro da pasta snipplets/forms. O código seria assim:
<div class="form-group {{ select_group_custom_class }}">
{% if select_label %}
<label {% if select_label_id%}id="{{ select_label_id }}"{% endif %} class="form-label {{ select_label_custom_class }}" {% if select_for %}for="{{ select_for }}"{% endif %}>{{ select_label_name }}</label>
{% endif %}
<select
{% if select_id %}id="{{ select_id }}"{% endif %}
class="form-select {{ select_custom_class }} {% if select_inline %}form-control-inline{% endif %}"
{% if select_data %}data-{{select_data}}="{{select_data_value}}"{% endif %}
{% if select_name %}name="{{ select_name }}"{% endif %}
{% if select_aria_label %}aria-label="{{ select_aria_label }}"{% endif %}>
{% block select_options %}
{% endblock select_options %}
</select>
<div class="form-select-icon">
{% include "snipplets/svg/chevron-down.tpl" with {svg_custom_class: "icon-inline icon-w-14 icon-lg svg-icon-text"} %}
</div>
</div>6. Agora precisamos criar o snipplet para o componente modal ou popup dentro da pasta snipplets. Este tpl se chama modal.tpl e o código é:
{# /*============================================================================
#Modal
==============================================================================*/
#Properties
// ID
// Position - Top, Right, Bottom, Left
// Transition - Slide and Fade
// Width - Full and Box
// modal_form_action - For modals that has a form
#Head
// Block - modal_head
#Body
// Block - modal_body
#Footer
// Block - modal_footer
#}
{% set modal_overlay = modal_overlay | default(true) %}
<div id="{{ modal_id }}" class="js-modal {% if modal_mobile_full_screen %}js-fullscreen-modal{% endif %} modal modal-{{ modal_class }} modal-{{modal_position}} transition-{{modal_transition}} modal-{{modal_width}} transition-soft" style="display: none;">
{% if modal_form_action %}
<form action="{{ modal_form_action }}" method="post" class="{{ modal_form_class }}" {% if modal_form_hook %}data-store="{{ modal_form_hook }}"{% endif %}>
{% endif %}
<div class="js-modal-close {% if modal_mobile_full_screen %}js-fullscreen-modal-close{% endif %} modal-header">
<span class="modal-close">
{% include "snipplets/svg/times.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %}
</span>
{% block modal_head %}{% endblock %}
</div>
<div class="modal-body">
{% block modal_body %}{% endblock %}
</div>
{% if modal_footer %}
<div class="modal-footer d-md-block">
{% block modal_foot %}{% endblock %}
</div>
{% endif %}
{% if modal_form_action %}
</form>
{% endif %}
</div>7. Finalmente, em layout.tpl adicionamos o seguinte código para levantar o conteúdo do popup:
{# Quickshop modal #}
{% snipplet "grid/quick-shop.tpl" %}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-async.tpl
Se em seu layout você usar um stylesheet CSS assíncrono, precisaremos do seguinte código dentro dela, mas se não for o caso, você pode adicioná-lo no arquivo com seu CSS principal.
{# /* // Buttons */ #}
.btn-transition {
position: relative;
overflow: hidden;
.transition-container {
position: absolute;
top: 50%;
left: 0;
width: 100%;
margin-top: -7px;
opacity: 0;
text-align: center;
@include prefix(transition, all 0.5s ease, webkit ms moz o);
cursor: not-allowed;
pointer-events: none;
&.active {
opacity: 1;
}
}
}
{# /* // Modals */ #}
.modal {
position: fixed;
top: 0;
display: block;
width: 80%;
height: 100%;
padding: 10px;
-webkit-overflow-scrolling: touch;
overflow-y: auto;
transition: all .2s cubic-bezier(.16,.68,.43,.99);
z-index: 20000;
&-header{
width: calc(100% + 20px);
margin: -10px 0 10px -10px;
padding: 10px 15px;
font-size: 20px;
}
&-footer{
padding: 10px;
clear: both;
}
&-full {
width: 100%;
}
&-docked-md{
width: 100%;
}
&-docked-small{
width: 80%;
}
&-top{
top: -100%;
left: 0;
}
&-bottom{
top: 100%;
left: 0;
}
&-left{
left: -100%;
}
&-right{
right: -100%;
}
&-centered{
height: 100%;
width: 100%;
}
&-top.modal-show,
&-bottom.modal-show {
top: 0;
}
&-bottom-sheet {
top: initial;
bottom: -100%;
height: auto;
&.modal-show {
top: initial;
bottom: 0;
height: auto;
}
}
&-left.modal-show {
left: 0;
}
&-right.modal-show {
right: 0;
}
&-close {
display: inline-block;
padding: 1px 5px 5px 0;
margin-right: 5px;
vertical-align: middle;
cursor: pointer;
}
.tab-group{
margin: 0 -10px 20px -10px;
}
}
.modal-overlay{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #00000047;
z-index: 10000;
}
@media (min-width: 768px) {
{# /* Modals */ #}
.modal{
&-centered{
height: 80%;
width: 80%;
left: 10%;
margin: 5% auto;
}
&-docked-md{
width: 500px;
&-centered{
left: calc(50% - 250px);
bottom: auto;
height: auto;
}
}
&-bottom-sheet {
top: 100%;
&.modal-show {
top: 0;
bottom: auto;
}
}
&-docked-small{
width: 350px;
}
}
}JS
1. JavaScript precisam ser adicionados no arquivo store.js.tpl (ou onde você tem suas funções JS). O código que precisamos é o seguinte:
O JS para os modais funcionarem:
{#/*============================================================================
#Modals
==============================================================================*/ #}
{% if settings.quick_shop %}
restoreQuickshopForm = function(){
{# Restore form to item when quickshop closes #}
{# Clean quickshop modal #}
jQueryNuvem("#quickshop-modal .js-item-product").removeClass("js-swiper-slide-visible js-item-slide");
jQueryNuvem("#quickshop-modal .js-quickshop-container").attr( { 'data-variants' : '' , 'data-quickshop-id': '' } );
jQueryNuvem("#quickshop-modal .js-item-product").attr('data-product-id', '');
{# Wait for modal to become invisible before removing form #}
setTimeout(function(){
var $quickshop_form = jQueryNuvem("#quickshop-form").find('.js-product-form');
var $item_form_container = jQueryNuvem(".js-quickshop-opened").find(".js-item-variants");
$quickshop_form.detach().appendTo($item_form_container);
jQueryNuvem(".js-quickshop-opened").removeClass("js-quickshop-opened");
},350);
};
{% endif %}
{# Full screen mobile modals back events #}
if (window.innerWidth < 768) {
{# Clean url hash function #}
cleanURLHash = function(){
const uri = window.location.toString();
const clean_uri = uri.substring(0, uri.indexOf("#"));
window.history.replaceState({}, document.title, clean_uri);
};
{# Go back 1 step on browser history #}
goBackBrowser = function(){
cleanURLHash();
history.back();
};
{# Clean url hash on page load: All modals should be closed on load #}
if(window.location.href.indexOf("modal-fullscreen") > -1) {
cleanURLHash();
}
{# Open full screen modal and url hash #}
jQueryNuvem(document).on("click", ".js-fullscreen-modal-open", function(e) {
e.preventDefault();
var modal_url_hash = jQueryNuvem(this).data("modalUrl");
window.location.hash = modal_url_hash;
});
{# Close full screen modal: Remove url hash #}
jQueryNuvem(document).on("click", ".js-fullscreen-modal-close", function(e) {
e.preventDefault();
goBackBrowser();
});
{# Hide panels or modals on browser backbutton #}
window.onhashchange = function() {
if(window.location.href.indexOf("modal-fullscreen") <= -1) {
{# Close opened modal #}
if(jQueryNuvem(".js-fullscreen-modal").hasClass("modal-show")){
{# Remove body lock only if a single modal is visible on screen #}
if(jQueryNuvem(".js-modal.modal-show").length == 1){
jQueryNuvem("body").removeClass("overflow-none");
}
var $opened_modal = jQueryNuvem(".js-fullscreen-modal.modal-show");
var $opened_modal_overlay = $opened_modal.prev();
$opened_modal.removeClass("modal-show");
setTimeout(() => $opened_modal.hide(), 500);
$opened_modal_overlay.fadeOut(500);
{% if settings.quick_shop %}
restoreQuickshopForm();
{% endif %}
}
}
}
}
jQueryNuvem(document).on("click", ".js-modal-open", function(e) {
e.preventDefault();
var modal_id = jQueryNuvem(this).data('toggle');
var $overlay_id = jQueryNuvem('.js-modal-overlay[data-modal-id="' + modal_id + '"]');
if (jQueryNuvem(modal_id).hasClass("modal-show")) {
let modal = jQueryNuvem(modal_id).removeClass("modal-show");
setTimeout(() => modal.hide(), 500);
} else {
{# Lock body scroll if there is no modal visible on screen #}
if(!jQueryNuvem(".js-modal.modal-show").length){
jQueryNuvem("body").addClass("overflow-none");
}
$overlay_id.fadeIn(400);
jQueryNuvem(modal_id).detach().appendTo("body");
$overlay_id.detach().insertBefore(modal_id);
jQueryNuvem(modal_id).show().addClass("modal-show");
}
});
jQueryNuvem(document).on("click", ".js-modal-close", function(e) {
e.preventDefault();
{# Remove body lock only if a single modal is visible on screen #}
if(jQueryNuvem(".js-modal.modal-show").length == 1){
jQueryNuvem("body").removeClass("overflow-none");
}
var $modal = jQueryNuvem(this).closest(".js-modal");
var modal_id = $modal.attr('id');
var $overlay_id = jQueryNuvem('.js-modal-overlay[data-modal-id="#' + modal_id + '"]');
$modal.removeClass("modal-show");
setTimeout(() => $modal.hide(), 500);
$overlay_id.fadeOut(500);
{% if settings.quick_shop %}
restoreQuickshopForm();
{% endif %}
{# Close full screen modal: Remove url hash #}
if ((window.innerWidth < 768) && (jQueryNuvem(this).hasClass(".js-fullscreen-modal-close"))) {
goBackBrowser();
}
});
jQueryNuvem(document).on("click", ".js-modal-overlay", function(e) {
e.preventDefault();
{# Remove body lock only if a single modal is visible on screen #}
if(jQueryNuvem(".js-modal.modal-show").length == 1){
jQueryNuvem("body").removeClass("overflow-none");
}
var modal_id = jQueryNuvem(this).data('modalId');
let modal = jQueryNuvem(modal_id).removeClass("modal-show");
setTimeout(() => modal.hide(), 500);
jQueryNuvem(this).fadeOut(500);
{% if settings.quick_shop %}
restoreQuickshopForm();
{% endif %}
if (jQueryNuvem(this).hasClass("js-fullscreen-overlay") && (window.innerWidth < 768)) {
cleanURLHash();
}
});O JS para atualizar as informações do produto ao alterar variantes:
jQueryNuvem(document).on("change", ".js-variation-option", function(e) {
var $parent = jQueryNuvem(this).closest(".js-product-variants");
var $variants_group = jQueryNuvem(this).closest(".js-product-variants-group");
var $quickshop_parent_wrapper = jQueryNuvem(this).closest(".js-quickshop-container");
{# If quickshop is used from modal, use quickshop-id from the item that opened it #}
if($quickshop_parent_wrapper.hasClass("js-quickshop-modal")){
var quick_id = jQueryNuvem(".js-quickshop-opened .js-quickshop-container").data("quickshopId");
}else{
var quick_id = $quickshop_parent_wrapper.data("quickshopId");
}
if($parent.hasClass("js-product-quickshop-variants")){
var $quickshop_parent = jQueryNuvem(this).closest(".js-item-product");
{# Target visible slider item if necessary #}
if($quickshop_parent.hasClass("js-item-slide")){
var $quickshop_variant_selector = '.js-swiper-slide-visible .js-quickshop-container[data-quickshop-id="'+quick_id+'"]';
}else{
var $quickshop_variant_selector = '.js-quickshop-container[data-quickshop-id="'+quick_id+'"]';
}
LS.changeVariant(changeVariant, $quickshop_variant_selector);
} else {
LS.changeVariant(changeVariant, '#single-product');
}
{# Offer and discount labels update #}
var $this_product_container = jQueryNuvem(this).closest(".js-product-container");
if($this_product_container.hasClass("js-quickshop-container")){
var this_quickshop_id = $this_product_container.attr("data-quickshop-id");
var $this_product_container = jQueryNuvem('.js-product-container[data-quickshop-id="'+this_quickshop_id+'"]');
}
var $this_compare_price = $this_product_container.find(".js-compare-price-display");
var $this_price = $this_product_container.find(".js-price-display");
var $installment_container = $this_product_container.find(".js-product-payments-container");
var $installment_text = $this_product_container.find(".js-max-installments-container");
var $this_add_to_cart = $this_product_container.find(".js-prod-submit-form");
// Get the current product discount percentage value
var current_percentage_value = $this_product_container.find(".js-offer-percentage");
// Get the current product price and promotional price
var compare_price_value = $this_compare_price.html();
var price_value = $this_price.html();
// Calculate new discount percentage based on difference between filtered old and new prices
const percentageDifference = window.moneyDifferenceCalculator.percentageDifferenceFromString(compare_price_value, price_value);
if(percentageDifference){
$this_product_container.find(".js-offer-percentage").text(percentageDifference);
$this_product_container.find(".js-offer-label").css("display" , "table");
}
if ($this_compare_price.css("display") == "none" || !percentageDifference) {
$this_product_container.find(".js-offer-label").hide();
}
if ($this_add_to_cart.hasClass("nostock")) {
$this_product_container.find(".js-stock-label").show();
}
else {
$this_product_container.find(".js-stock-label").hide();
}
if ($this_price.css('display') == 'none'){
$installment_container.hide();
$installment_text.hide();
}else{
$installment_text.show();
}
});Em seguida, o JS para o modal eleva o conteúdo correspondente e também é sincronizado com a funcionalidade Cores no item do produto, se houver:
{% if settings.product_color_variants %}
{# Product color variations #}
jQueryNuvem(document).on("click", ".js-color-variant", function(e) {
e.preventDefault();
$this = jQueryNuvem(this);
var option_id = $this.data('option');
$selected_option = $this.closest('.js-item-product').find('.js-variation-option option').filter(function(el) {
return el.value == option_id;
});
$selected_option.prop('selected', true).trigger('change');
var available_variant = jQueryNuvem(this).closest(".js-quickshop-container").data('variants');
var available_variant_color = jQueryNuvem(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(jQueryNuvem(this), otherOption, otherOptions[0]);
changeSelect(jQueryNuvem(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(el) {
return el.value == optionToSelect;
});
selected_option.prop('selected', true).trigger('change');
}
}
{% endif %}
{% if settings.product_color_variants or settings.quick_shop %}
{# Product quickshop for color variations #}
LS.registerOnChangeVariant(function(variant){
{# Show product image on color change #}
var current_image = jQueryNuvem('.js-item-product[data-product-id="'+variant.product_id+'"] .js-item-image');
current_image.attr('srcset', variant.image_url);
});
{% endif %}
{% if settings.quick_shop %}
jQueryNuvem(document).on("click", ".js-quickshop-modal-open", function (e) {
e.preventDefault();
var $this = jQueryNuvem(this);
if($this.hasClass("js-quickshop-slide")){
jQueryNuvem("#quickshop-modal .js-item-product").addClass("js-swiper-slide-visible js-item-slide");
}
LS.fillQuickshop($this);
});
{# Get width of the placeholder button #}
var productButttonWidth = jQueryNuvem(".js-addtocart-placeholder-inline").prev(".js-addtocart").innerWidth();
jQueryNuvem(".js-addtocart-placeholder-inline").width(productButttonWidth-20);
{% endif %}Finalmente, o JS que atualiza o botão e a notificação ao adicionar ao carrinho:
jQueryNuvem(document).on("click", ".js-addtocart:not(.js-addtocart-placeholder)", function (e) {
{# Button variables for transitions on add to cart #}
var $productContainer = jQueryNuvem(this).closest('.js-product-container');
var $productVariants = $productContainer.find(".js-variation-option");
var $productButton = $productContainer.find("input[type='submit'].js-addtocart");
var $productButtonPlaceholder = $productContainer.find(".js-addtocart-placeholder");
var $productButtonText = $productButtonPlaceholder.find(".js-addtocart-text");
var $productButtonAdding = $productButtonPlaceholder.find(".js-addtocart-adding");
var $productButtonSuccess = $productButtonPlaceholder.find(".js-addtocart-success");
{# Define if event comes from quickshop or product page #}
var isQuickShop = $productContainer.hasClass('js-quickshop-container');
if (!isQuickShop) {
if(jQueryNuvem(".js-product-slide-img.js-active-variant").length) {
var imageSrc = $productContainer.find('.js-product-slide-img.js-active-variant').data('srcset').split(' ')[0];
} else {
var imageSrc = $productContainer.find('.js-product-slide-img').attr('srcset').split(' ')[0];
}
var name = $productContainer.find('.js-product-name').text();
var price = $productContainer.find('.js-price-display').text();
} else {
var imageSrc = jQueryNuvem(this).closest('.js-quickshop-container').find('img').attr('srcset');
var name = $productContainer.find('.js-item-name').text();
var price = $productContainer.find('.js-price-display').text().trim();
}
var quantity = $productContainer.find('.js-quantity-input').val();
var addedToCartCopy = "{{ 'Agregar al carrito' | translate }}";
if (!jQueryNuvem(this).hasClass('contact')) {
{% if settings.ajax_cart %}
e.preventDefault();
{% endif %}
{# Hide real button and show button placeholder during event #}
$productButton.hide();
$productButtonPlaceholder.show().addClass("active");
$productButtonText.removeClass("active");
setTimeout(function(){
$productButtonAdding.addClass("active");
},300);
{% if settings.ajax_cart %}
var callback_add_to_cart = function(){
{# Animate cart amount #}
jQueryNuvem(".js-cart-widget-amount").addClass("beat");
setTimeout(function(){
jQueryNuvem(".js-cart-widget-amount").removeClass("beat");
},4000);
{# Fill notification info #}
jQueryNuvem('.js-cart-notification-item-img').attr('srcset', imageSrc);
jQueryNuvem('.js-cart-notification-item-name').text(name);
jQueryNuvem('.js-cart-notification-item-quantity').text(quantity);
jQueryNuvem('.js-cart-notification-item-price').text(price);
if($productVariants.length){
var output = [];
$productVariants.each( function(el){
var variants = jQueryNuvem(el);
output.push(variants.val());
});
jQueryNuvem(".js-cart-notification-item-variant-container").show();
jQueryNuvem(".js-cart-notification-item-variant").text(output.join(', '))
}else{
jQueryNuvem(".js-cart-notification-item-variant-container").hide();
}
{# Set products amount wording visibility #}
var cartItemsAmount = jQueryNuvem(".js-cart-widget-amount").text();
if(cartItemsAmount > 1){
jQueryNuvem(".js-cart-counts-plural").show();
jQueryNuvem(".js-cart-counts-singular").hide();
}else{
jQueryNuvem(".js-cart-counts-singular").show();
jQueryNuvem(".js-cart-counts-plural").hide();
}
{# Show button placeholder with transitions #}
$productButtonAdding.removeClass("active");
setTimeout(function(){
$productButtonSuccess.addClass("active");
},300);
setTimeout(function(){
$productButtonSuccess.removeClass("active");
setTimeout(function(){
$productButtonText.addClass("active");
},300);
$productButtonPlaceholder.removeClass("active");
},2000);
setTimeout(function(){
$productButtonPlaceholder.hide();
$productButton.css('display' , 'inline-block');
},4000);
$productContainer.find(".js-added-to-cart-product-message").slideDown();
if (isQuickShop) {
jQueryNuvem("#quickshop-modal").removeClass('modal-show');
jQueryNuvem(".js-modal-overlay[data-modal-id='#quickshop-modal']").hide();
jQueryNuvem("body").removeClass("overflow-none");
restoreQuickshopForm();
if (window.innerWidth < 768) {
cleanURLHash();
}
}
{# Show notification and hide it only after second added to cart #}
setTimeout(function(){
jQueryNuvem(".js-alert-added-to-cart").show().addClass("notification-visible").removeClass("notification-hidden");
},500);
if (!cookieService.get('first_product_added_successfully')) {
cookieService.set('first_product_added_successfully', 1, 7 );
} else{
setTimeout(function(){
jQueryNuvem(".js-alert-added-to-cart").removeClass("notification-visible").addClass("notification-hidden");
setTimeout(function(){
jQueryNuvem('.js-cart-notification-item-img').attr('src', '');
jQueryNuvem(".js-alert-added-to-cart").hide();
},2000);
},8000);
}
{# Update shipping input zipcode on add to cart #}
{# Use zipcode from input if user is in product page, or use zipcode cookie if is not #}
if (jQueryNuvem("#product-shipping-container .js-shipping-input").val()) {
zipcode_on_addtocart = jQueryNuvem("#product-shipping-container .js-shipping-input").val();
jQueryNuvem("#cart-shipping-container .js-shipping-input").val(zipcode_on_addtocart);
jQueryNuvem(".js-shipping-calculator-current-zip").text(zipcode_on_addtocart);
} else if (cookieService.get('calculator_zipcode')){
var zipcode_from_cookie = cookieService.get('calculator_zipcode');
jQueryNuvem('.js-shipping-input').val(zipcode_from_cookie);
jQueryNuvem(".js-shipping-calculator-current-zip").text(zipcode_from_cookie);
}
}
var callback_error = function(){
{# Restore real button visibility in case of error #}
$productButtonPlaceholder.removeClass("active");
$productButtonText.fadeIn("active");
$productButtonAdding.removeClass("active");
$productButtonPlaceholder.hide();
$productButton.css('display' , 'inline-block');
}
$prod_form = jQueryNuvem(this).closest("form");
LS.addToCartEnhanced(
$prod_form,
'{{ "Agregar al carrito" | translate }}',
'{{ "Agregando..." | translate }}',
'{{ "¡Uy! No tenemos más stock de este producto para agregarlo al carrito." | translate }}',
{{ store.editable_ajax_cart_enabled ? 'true' : 'false' }},
callback_add_to_cart,
callback_error
);
{% endif %}
}
});Configurações
No arquivo config/settings.txt, adicionaremos um checkbox para ativar e desativar a funcionalidade. Vamos colocá-lo na seção Lista de produtos.
title
title = Compra rápida
checkbox
name = quick_shop
description = Permitir que tus clientes puedan agregar productos a su carrito rápidamente desde el listado de productosTraduções
Nesta etapa, adicionamos os textos para as traduções no arquivo config/translations.txt
es "Compra rápida" pt "Compra rápida" es_mx "Compra rápida" es "Permitir que tus clientes puedan agregar productos a su carrito rápidamente desde el listado de productos" pt "Permitir que seus clientes possam agregar produtos ao seu carrinho rapidamente na lista de produtos" es_mx "Permitir que tus clientes puedan agregar productos a su carrito rápidamente desde el listado de productos" es "Compra rápida de" pt "Compra rápida de" en "Quickshop of" es_mx "Compra rápida de"
Ativação
Por fim, para ativar esses banners, você pode fazer isso no Adminstrador Nuvem, na seção Personalizar meu layout atual na Lista de produtos:

