Navegação de categorias mobile

Dependendo do seu layout, talvez seja necessário ter as categorias mais visíveis no celular. Neste tutorial, vamos ver como adicionar uma navegação apenas para categorias, veja como fica:

HTML

A primeira coisa que vamos fazer é criar a pasta de navigation dentro da pasta snipplets.

1. Na navigation, adicionamos os dois snipplets a seguir com seus respectivos códigos:

navigation-categories.tpl

Neste arquivo listamos todas as categorias e subcategorias da loja.

{# All store categories and subcategories with links #}

{% for category in categories %}
    {% if category.subcategories %}
        <a href="#" class="js-modal-open modal-list-item" data-toggle="#category-{{ category.id }}">
            {{ category.name }}
              {% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-lg modal-list-icon"} %}
        </a>
    {% else %}
        <a href="{{ category.url }}" class="modal-list-item">
            {{ category.name }}
        </a>
    {% endif %}
{% endfor %}

navigation-categories-panel.tpl

Aqui nós chamamos o componente modal ou popup por meio de um embed. Este snipplet chama a si mesmo iterando por cada subcategoria.

{# Modals for each subcategory #}


{% embed "snipplets/modal.tpl" with{modal_id: 'category-' ~ category.id, modal_class: 'categories', modal_position: 'right', modal_transition: 'slide', modal_width: 'full', modal_head: true, modal_overlay: false, modal_close_left: true, modal_helper: 'pb-5'} %}
    {% block modal_head %}
        {{ category.name }}
    {% endblock %}
    {% block modal_body %}
        <a href="{{ category.url }}" class="modal-list-item font-weight-bold">
            {{ "Ver toda esta categoría" | translate }}
        </a>
        {% snipplet "navigation/navigation-categories.tpl" with categories = category.subcategories %}
    {% endblock %}
{% endembed %}


{% for subcategory in category.subcategories %}
    {% snipplet "navigation/navigation-categories-panel.tpl" with category = subcategory %}
{% endfor %}

2. Temos que chamar o link que abre o popup que engloba todas as categorias. No layout Base nós colocamos dentro do snipplet header.tpl dentro da pasta snipplets/header, mas no seu caso pode ser outro arquivo. O importante é que esteja dentro da navegação principal do site.

   {# Main tab link #}


    <a href="#" id="toggle-mobile-categories" class="js-modal-open btn btn-block nav-tab" data-toggle="#main-categories">
        {{ 'Categorías' | translate }}
        {% include "snipplets/svg/chevron-down.tpl" with {svg_custom_class: "js-toggle-icon icon-inline icon-lg"} %}
        {% include "snipplets/svg/chevron-up.tpl" with {svg_custom_class: "js-toggle-icon icon-inline icon-lg hidden"} %}
    </a>

3. Em seguida, adicionamos a classe js-head-main ao div que contém todo o had do site.

4. Dentro do mesmo tpl que editamos na etapa 2, mas fora da navegação do site, chamaremos os snipplets criados na etapa 1, da seguinte maneira:

{# Main categories modal #}

<div id="nav-mobile-categories" style="display: none;">
    {% embed "snipplets/modal.tpl" with{modal_id: 'main-categories', modal_class: 'categories', modal_position: 'top', modal_transition: 'slide', modal_width: 'full', modal_overlay: false} %}
        {% block modal_head %}
            
        {% endblock %}
        {% block modal_body %}
            <a href="{{ store.products_url }}" class="modal-list-item font-weight-bold">
                {{ 'Ver todos los productos' | translate }}
            </a>
            {% snipplet "navigation/navigation-categories.tpl" %}
        {% endblock %}
    {% endembed %}

    {# Subcategories modals #}

    {% for category in categories %}
        {% snipplet "navigation/navigation-categories-panel.tpl" %}
    {% endfor %}
</div>

Podemos observar que novamente usamos um embed para o modal das categorias principais e, em seguida, adicionamos ao resto dos modais para as outras subcategorias, usando um for.

5.  Agora precisamos criar o snipplet para o componente modal ou pop-up dentro da pasta snipplets. Este tpl é chamado 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
    // modal_overlay - used for modals with an overlay behind
    // modal_head - to show a header on the modal
    // modal_close_left - If true shows arrow left on modal header, else shows an X to close


#Head
    // Block - modal_head
#Body
    // Block - modal_body
#Footer
    // Block - modal_footer


#}

<div id="{{ modal_id }}" class="js-modal modal modal-{{ modal_class }} modal-{{modal_position}} transition-{{modal_transition}} modal-{{modal_width}} transition-soft {% if not modal_overlay %}js-no-overlay modal-no-overlay{% endif %}" style="display: none;">
    {% if modal_form_action %}
    <form action="{{ modal_form_action }}" method="post" class="{{ modal_form_class }}">
    {% endif %}
    {% if modal_head %}
        <div class="js-modal-close modal-header">
            <span class="modal-close">
                {% if modal_close_left %}
                    {% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %}
                {% else %}
                    {% include "snipplets/svg/times.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %}
                {% endif %}
            </span>
            {% block modal_head %}{% endblock %}
        </div>
    {% endif %}
    <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>

Se você já estava usando este snipplet antes, provavelmente precisará atualizar os outros locais onde o usou, adicionando os parâmetros modal_head: true e modal_overlay: true na incorporação.

5.  Por último, para a parte HTML, precisamos adicionar uma pasta SVG dentro da pasta snipplets, onde adicionaremos os sniplpets para os ícones usados. Neste caso, eles serão os seguintes:

times.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>

chevron-down.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M441.9 167.3l-19.8-19.8c-4.7-4.7-12.3-4.7-17 0L224 328.2 42.9 147.5c-4.7-4.7-12.3-4.7-17 0L6.1 167.3c-4.7 4.7-4.7 12.3 0 17l209.4 209.4c4.7 4.7 12.3 4.7 17 0l209.4-209.4c4.7-4.7 4.7-12.3 0-17z"/></svg>

chevron-up.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M6.101 359.293L25.9 379.092c4.686 4.686 12.284 4.686 16.971 0L224 198.393l181.13 180.698c4.686 4.686 12.284 4.686 16.971 0l19.799-19.799c4.686-4.686 4.686-12.284 0-16.971L232.485 132.908c-4.686-4.686-12.284-4.686-16.971 0L6.101 342.322c-4.687 4.687-4.687 12.285 0 16.971z"/></svg>

chevron-right.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M24.707 38.101L4.908 57.899c-4.686 4.686-4.686 12.284 0 16.971L185.607 256 4.908 437.13c-4.686 4.686-4.686 12.284 0 16.971L24.707 473.9c4.686 4.686 12.284 4.686 16.971 0l209.414-209.414c4.686-4.686 4.686-12.284 0-16.971L41.678 38.101c-4.687-4.687-12.285-4.687-16.971 0z"/></svg>

chevron-left.tpl

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 512"><path d="M231.293 473.899l19.799-19.799c4.686-4.686 4.686-12.284 0-16.971L70.393 256 251.092 74.87c4.686-4.686 4.686-12.284 0-16.971L231.293 38.1c-4.686-4.686-12.284-4.686-16.971 0L4.908 247.515c-4.686 4.686-4.686 12.284 0 16.971L214.322 473.9c4.687 4.686 12.285 4.686 16.971-.001z"/></svg>

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. Adicionamos o seguinte SASS de cores em style-colors.scss.tpl (ou no 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 design:

@mixin prefix($property, $value, $prefixes: ()) {
  @each $prefix in $prefixes {
      #{'-' + $prefix + '-' + $property}: $value;
  }
    #{$property}: $value;
}


{# /* // Modals */ #}


.modal{
  color: $main-foreground;
  background-color:$main-background;
  &-list-item{
    border-bottom: 1px solid rgba($main-foreground, .1);
  }
}


{# /* // Buttons */ #}


.btn{
  text-decoration: none;
  text-align: center;
  border: 0;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  text-transform: uppercase;
  background: none;
  @include prefix(transition, all 0.4s ease, webkit ms moz o);
  &:hover,
  &:focus{
    outline: 0;
    opacity: 0.8;
  }
  &[disabled],
  &[disabled]:hover{
    opacity: 0.5;
    cursor: not-allowed;
    outline: 0;
  }
}

  .nav-tab{
    border-top: 1px solid $main-foreground;
  }

2. 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, mas se não for o caso, você poderá unificar o CSS dos passos 2 e 3 em um único arquivo.

.nav-tab{
  padding: 10px;
  font-weight: bold;
}

3. Adicione os estilos no arquivo static/style-async.tpl

{# /* // 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-sm{
    width: 100%;
  }
  &-docked-small{
    width: 80%;
  }
  &-top{
    top: -100%;
    left: 0;
  }
  &-bottom{
    top: 100%;
    left: 0;
  }
  &-left{
    left: -100%;
  }
  &-right{
    right: -100%;
    &.modal-categories{
      top: auto;
    }
  }
  &-centered{
    height: 100%;
    width: 100%;
  }
  &-top.modal-show,
  &-bottom.modal-show {
    top: 0;
    &.modal-categories{
      top: auto;
      box-shadow: -4px 0 17px 0 rgba(0, 0, 0, 0.23);
    }
  }
  &-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;
  }
  &-list-item{
    position: relative;
    display: block;
    width: 100%;
    padding: 15px 5px;
    text-align: left;
    font-size: 14px;
  }
  &-list-icon{
    position: absolute;
    right: 10px;
  }
  &-categories .modal-header{
    margin-bottom: 0;
  }
}


.modal-overlay{
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #00000047;
  z-index: 10000;
}

JS

⚠️ A partir do dia 30 de janeiro de 2023, a biblioteca jQuery será removida do código de nossas lojas, portanto, a função "$" não poderá ser utilizada.

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

{# 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);

            }
        }
    }
}

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

    {# 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 (jQueryNuvem(this).hasClass("js-fullscreen-overlay") && (window.innerWidth < 768)) {
        cleanURLHash();
    }
});

Traduções

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

es "Categorías"
pt "Categorias"
en "Categories"
es_mx "Categorías"


es "Ver todos los productos"
pt "Ver todos os produtos"
en "See all products"
es_mx "Ver todos los productos"


es "Ver toda esta categoría"
pt "Ver tudo desta categoria"
en "See all this category"
es_mx "Ver toda esta categoría"

Parabéns! Você já tem a navegação de categorias para celular no seu layout.