Secciones con drag and drop

En este tutorial vamos a ver cómo podemos hacer para que las secciones de la página de inicio puedan cambiarse de posición con "drag and drop"

Vamos a contemplar como ejemplo 3 secciones "arrastrables":

  • Carrusel de imágenes
  • Productos destacados
  • Banners de categorías

 Y una sección "fija", como el Pop-up promocional.

HTML

1. Lo primero que vamos a hacer es crear la carpeta home dentro de la carpeta snipplets

Dentro de la carpeta creada, agregamos un snipplet con el nombre home-section-switch.tpl con el siguiente código. Básicamente se muestra un "empty placeholder" si la sección no tiene contenido cargado.

{% if section_select == 'slider' %}
    
    {#  **** Home slider ****  #}
    <section data-store="home-slider">
        {% if show_help or (show_component_help and not (has_main_slider or has_mobile_slider)) %}
            {% snipplet 'defaults/home/slider_help.tpl' %}
        {% else %}
            {% include 'snipplets/home/home-slider.tpl' %}
            {% if has_mobile_slider %}
                {% include 'snipplets/home/home-slider.tpl' with {mobile: true} %}
            {% endif %}
        {% endif %}
    </section>

{% elseif section_select == 'products' %}

    {#  **** Featured products ****  #}
    {% if show_help or (show_component_help and not has_products) %}
        {% include 'snipplets/defaults/home/featured_products_help.tpl' %}
    {% else %}
        {% include 'snipplets/home/home-featured-products.tpl' %}
    {% endif %}

{% elseif section_select == 'categories' %}

    {#  **** Categories banners ****  #}
    {% if show_help or (show_component_help and not has_category_banners) %}
        {% include 'snipplets/defaults/home/banners_help.tpl' with {
            banner_title: 'Categoría' | translate,
            banner_help_text: 'Podés destacar categorías de tu tienda desde' | translate,
            banner_help_section: 'Banners de categorías' | translate,
            data_store: 'home-banner-categories',
            banners_amount: 3} 
        %}
    {% else %}
        {% include 'snipplets/home/home-banners.tpl' with {'textoverimage': true} %}
    {% endif %}

{% endif %}

Luego, un segundo snipplet con el nombre home-slider.tpl

{% set has_main_slider = settings.slider and settings.slider is not empty %}
{% set has_mobile_slider = settings.toggle_slider_mobile and settings.slider_mobile and settings.slider_mobile is not empty %}

{% if not mobile %}
<div class="js-home-main-slider-container {% if not has_main_slider %}hidden{% endif %}">
{% endif %}
    <div class="{% if mobile %}js-home-mobile-slider{% else %}js-home-main-slider{% endif %}-visibility section-slider {% if has_main_slider and has_mobile_slider %}{% if mobile %}d-md-none{% else %}d-none d-md-block{% endif %}{% elseif not settings.toggle_slider_mobile and mobile %}hidden{% endif %}">
        <div class="js-home-slider{% if mobile %}-mobile{% endif %} nube-slider-home swiper-container swiper-container-horizontal">
            <div class="swiper-wrapper">
                {% if mobile %}
                    {% set slider = settings.slider_mobile %}
                {% else %}
                    {% set slider = settings.slider %}
                {% endif %}
                {% for slide in slider %}
                    <div class="swiper-slide slide-container">
                        {% set apply_lazy_load = not (loop.first and ((has_main_slider and not has_mobile_slider) or (has_mobile_slider and mobile))) %}

                        {% if apply_lazy_load %}
                            {% set slide_src = slide.image | static_url | settings_image_url('tiny') %}
                        {% else %}
                            {% set slide_src = slide.image | static_url | settings_image_url('xlarge') %}
                        {% endif %}

                        {% if slide.link %}
                            <a href="{{ slide.link | setting_url }}" aria-label="{{ 'Carrusel' | translate }} {{ loop.index }}">
                        {% endif %}
                        {% set has_text =  slide.title or slide.description or slide.button %}
                            <div class="slider-slide">
                                <img 
                                    {% if slide.width and slide.height %} width="{{ slide.width }}" height="{{ slide.height }}" {% endif %}
                                    src="{{ slide_src }}"
                                    {% if apply_lazy_load %}data-{% endif %}srcset="{{ slide.image | static_url | settings_image_url('xlarge') }} 1400w, {{ slide.image | static_url | settings_image_url('1080p') }} 1920w" class="slider-image {% if apply_lazy_load %}swiper-lazy blur-up-big{% endif %}" alt="{{ 'Carrusel' | translate }} {{ loop.index }}"/>
                                {% if has_text %}
                                    <div class="swiper-text swiper-{{ slide.color }}">
                                        {% if slide.title %}
                                            <div class="swiper-title h1">{{ slide.title }}</div>
                                        {% endif %}
                                        {% if slide.description %}
                                            <div class="swiper-description h5 font-weight-normal mt-3">{{ slide.description }}</div>
                                        {% endif %}
                                        {% if slide.button and slide.link %}
                                            <div class="btn btn-small swiper-btn mt-4">{{ slide.button }}</div>
                                        {% endif %}
                                    </div>
                                {% endif %}
                            </div>
                        {% if slide.link %}
                            </a>
                        {% endif %}
                    </div>
                {% endfor %}
            </div>
            <div class="js-swiper-home-control js-swiper-home-pagination{% if mobile %}-mobile{% endif %} swiper-pagination swiper-pagination-bullets swiper-pagination-white">
                {% if slider | length > 1 and not params.preview %}
                    {% for slide in slider %}
                        <span class="swiper-pagination-bullet"></span>
                    {% endfor %}
                {% endif %}
            </div>
            <div class="js-swiper-home-control js-swiper-home-prev{% if mobile %}-mobile{% endif %} swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-lg svg-icon-invert"} %}</div>
            <div class="js-swiper-home-control js-swiper-home-next{% if mobile %}-mobile{% endif %} swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-lg svg-icon-invert"} %}</div>
        </div>
    </div>
{% if not mobile %}
</div>
{% endif %}

Un tercero con el nombre home-banners.tpl

<section class="section-banners-home" data-store="home-banner-categories">
    <div class="container{% if settings.banners_full %}-fluid p-0{% endif %}">
        <div class="row {% if settings.banners_full %}no-gutters{% endif %} align-items-center">
            {% set num_banners = 0 %}
            {% for banner in ['banner_01', 'banner_02'] %}
                {% set banner_show = attribute(settings,"#{banner}_show") %}
                {% set banner_title = attribute(settings,"#{banner}_title") %}
                {% set banner_button_text = attribute(settings,"#{banner}_button") %}
                {% set has_banner =  banner_show and (banner_title or banner_description or "#{banner}.jpg" | has_custom_image) %}
                {% if has_banner %}
                    {% set num_banners = num_banners + 1 %}
                {% endif %}
            {% endfor %}

            {% for banner in ['banner_01', 'banner_02', 'banner_03'] %}
                {% set banner_show = attribute(settings,"#{banner}_show") %}
                {% set banner_title = attribute(settings,"#{banner}_title") %}
                {% set banner_description = attribute(settings,"#{banner}_description") %}
                {% set banner_button_text = attribute(settings,"#{banner}_button") %}
                {% set banner_url = attribute(settings,"#{banner}_url") %}
                {% set has_banner =  banner_show and (banner_title or banner_description or "#{banner}.jpg" | has_custom_image) %}
                {% set has_banner_text =  banner_title or banner_description or banner_button_text %}
                {% if has_banner %}
                    <div class="col-md">
                        <div class="textbanner">
                            {% if banner_url %}
                                <a class="textbanner-link" href="{{ banner_url | setting_url }}"{% if banner_title %} title="{{ banner_title }}" aria-label="{{ banner_title }}"{% else %} title="{{ 'Banner de' | translate }} {{ store.name }}" aria-label="{{ 'Banner de' | translate }} {{ store.name }}"{% endif %}>
                            {% endif %}
                            <div class="textbanner-image{% if has_banner_text and textoverimage %} overlay{% endif %}">
                                <img class="textbanner-image-background lazyautosizes lazyload blur-up-big" src="{{ "#{banner}.jpg" | static_url | settings_image_url('tiny') }}" data-srcset="{{ "#{banner}.jpg" | static_url | settings_image_url('large') }} 480w, {{ "#{banner}.jpg" | static_url | settings_image_url('huge') }} 640w" data-sizes="auto" data-expand="-10" {% if banner_title %}alt="{{ banner_title }}"{% else %}alt="{{ 'Banner de' | translate }} {{ store.name }}"{% endif %} />
                            </div>
                            <div class="textbanner-text{% if textoverimage %} over-image{% endif %}">
                                {% if banner_title %}
                                    <div class="h1 textbanner-title">{{ banner_title }}</div>
                                {% endif %}
                                {% if banner_description %}
                                    <div class="textbanner-paragraph">{{ banner_description }}</div>
                                {% endif %}
                                {% if banner_url and banner_button_text %}
                                    <button class="btn btn-line btn-small">{{ banner_button_text }}</button>
                                {% endif %}
                            </div>
                            {% if banner_url %}
                                </a>
                            {% endif %}
                        </div>
                    </div>
                {% endif %}
            {% endfor %}
        </div>
    </div>
</section>

Un cuarto snipplet con el nombre home-featured-products.tpl

<section class="section-featured-home" data-store="home-products-featured">
    <div class="container">
        <div class="row">
            {% if settings.featured_products_title %}
                <div class="col-12 text-center">
                    <h3>{{ settings.featured_products_title }}</h3>
                </div>
            {% endif %}
            <div class="col-12">
                <div class="js-swiper-featured swiper-container">
                    <div class="swiper-wrapper">
                        {% for product in sections.primary.products %}
                            {% include 'snipplets/grid/item.tpl' with {'slide_item': true} %}
                        {% endfor %}
                    </div>
                    <div class="js-swiper-featured-pagination swiper-pagination"></div>
                    <div class="js-swiper-featured-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div>
                    <div class="js-swiper-featured-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div>
                </div>
            </div>
        </div>
    </div>
</section>

Y por último, el snipplet home-popup.tpl

{% embed "snipplets/modal.tpl" with{modal_id: 'home-modal', modal_position: 'bottom', modal_transition: 'slide', modal_header: false, modal_footer: false, modal_width: 'centered', modal_class: 'centered-small', modal_close_class: 'invert mt-2 mr-3'  } %}
    {% block modal_body %}
        {% if "home_popup_image.jpg" | has_custom_image %}
            {% if settings.home_popup_url %}
            <a href="{{ settings.home_popup_url }}">
            {% endif %}
                <div class="text-center">
                    <img src="{{ 'images/empty-placeholder.png' | static_url }}" data-srcset='{{ "home_popup_image.jpg" | static_url | settings_image_url('huge') }} 640w, {{ "home_popup_image.jpg" | static_url | settings_image_url('original') }} 1024w' class="lazyload fade-in modal-img-full"/>
                </div>
            {% if settings.home_popup_url %}
            </a>
            {% endif %}
        {% endif %}
        {% if settings.home_popup_txt or settings.home_news_box %}
        <div class="align-items-center {% if not 'home_popup_image.jpg' | has_custom_image %}mt-3{% endif %}">
            {% if settings.home_popup_txt %}
                <div class="col-12">
                    {% if settings.home_popup_url %}
                    <a href="{{ settings.home_popup_url }}">
                    {% endif %}
                    <h3 class="text-center mt-3 {% if not settings.home_news_box %}mb-0{% endif %}">{{ settings.home_popup_txt }}</h3>
                    {% if settings.home_popup_url %}
                    </a>
                    {% endif %}
                </div>
            {% endif %}
            {% if settings.home_news_box %}
                <div class="col-12 newsletter">
                    <div id="news-popup-form-container">
                        <form id="news-popup-form" method="post" action="/winnie-pooh" class="js-news-form">
                            <div class="input-append">
                                {% embed "snipplets/forms/form-input.tpl" with{input_for: 'email', type_email: true, input_name: 'email', input_id: 'email', input_placeholder: 'Email' | translate, input_aria_label: 'Email' | translate } %}
                                {% endembed %}
                                <div class="winnie-pooh" style="display: none;">
                                    <label for="winnie-pooh-newsletter">{{ "No completar este campo" | translate }}</label>
                                    <input id="winnie-pooh-newsletter" type="text" name="winnie-pooh"/>
                                </div>
                                <input type="hidden" name="name" value="{{ "Sin nombre" | translate }}" />
                                <input type="hidden" name="message" value="{{ "Pedido de inscripción a newsletter" | translate }}" />
                                <input type="hidden" name="type" value="newsletter" />
                                <div class="js-news-spinner text-center" style="display: none;">
                                    {% include "snipplets/svg/sync-alt.tpl" with {svg_custom_class: "icon-inline svg-icon-text icon-spin icon-2x mb-3"} %}
                                </div>
                                <div style='display: none;' class="js-news-popup-success alert alert-success">{{ "¡Gracias por suscribirte! A partir de ahora vas a recibir nuestras novedades en tu email" | translate }}</div>
                                <div style='display: none;'class="js-news-popup-failed alert alert-danger">{{ "Necesitamos tu email para enviarte nuestras novedades." | translate }}</div>
                                <input type="submit" name="contact" class="btn newsletter-btn js-news-send" value='{{ "Enviar" | translate }}'>    
                            </div>
                        </form>
                    </div>
                </div>
            {% endif %}
        </div>
        {% endif %}
    {% endblock %}
{% endembed %}

2. Vamos a crear la carpeta defaults dentro de la carpeta snipplets

Dentro de la carpeta creada, agregamos el snipplet help_item.tpl que se va mostrar en el "empty placeholder".

{% set slide_item = slide_item | default(false) %}

<div class="{% if slide_item %}swiper-slide{% else %}col-6 col-md-3{% endif %} item item-product">
    <div class="item-image mb-2">
        <a href="{{ store.url }}/product/example" title="{{ "Producto de ejemplo" | translate }}">
            {% set type_value = 
                help_item_1 ? 'shirt' : 
                help_item_2 ? 'camera' :
                help_item_3 ? 'dress' :
                help_item_4 ? 'sneakers' :
                help_item_5 ? 'joystick' :
                help_item_6 ? 'shoes' :
                help_item_7 ? 'bag' :
                help_item_8 ? 'glasses' 
            %}
            {{ component('placeholders/product-placeholder',{
                    type: type_value,
                })
            }}
        </a>
        {% if help_item_1 %}
            <div class="labels">
                <div class="label label-primary">{{ "20% OFF" | translate }}</div>
            </div>
        {% elseif help_item_2 %}
            <div class="labels">
                <div class="label label-secondary">{{ "Envío gratis" | translate }}</div>
            </div>
        {% elseif help_item_3 %}
            <div class="labels">
                <div class="label label-primary">{{ "35% OFF" | translate }}</div>
            </div>
        {% elseif help_item_7 %}
            <div class="labels">
                <div class="label label-primary">{{ "20% OFF" | translate }}</div>
            </div>
        {% endif %}
    </div>
    <div class="item-description">
        <a href="{{ store.url }}/product/example" title="{{ "Producto de ejemplo" | translate }}" class="item-link">
            <div class="item-name mb-1">{{ "Producto de ejemplo" | translate }}</div>
            <div class="item-price-container mb-1">
                {% set help_item_price = 
                    help_item_1 ? '9600' : 
                    help_item_2 ? '68000' :
                    help_item_3 ? '18200' :
                    help_item_4 ? '32000' :
                    help_item_5 ? '24900' :
                    help_item_6 ? '42000' :
                    help_item_7 ? '36800' :
                    help_item_8 ? '12200' 
                %}
                {% if help_item_1 or help_item_3 or help_item_7 %}
                    {% set item_promotional_price = true %}
                    {% set help_item_price_compare = 
                        help_item_1 ? '120000' : 
                        help_item_3 ? '28000' :
                        help_item_7 ? '46000'
                    %}
                {% endif %}
                {% if store.country != 'BR' %}
                    {% set help_item_price = help_item_price ~ "00" %}
                    {% if item_promotional_price %}
                        {% set help_item_price_compare = help_item_price_compare ~ "00" %}
                    {% endif %}
                {% endif %}
                {% if item_promotional_price %}
                    <span class="price-compare">{{ help_item_price_compare | money }}</span>
                {% endif %}
                <span class="item-price">{{ help_item_price | money }}</span>
            </div>
        </a>
    </div>
</div>

3. Vamos a crear la carpeta home dentro de la carpeta snipplets/defaults

Dentro de la carpeta creada, agregamos el snipplet slider_help.tpl

{# Slider that work as example #}

<div class="section-slider">
    <div class="js-home-empty-slider nube-slider-home swiper-container">
        <div class="swiper-wrapper">
            {% for i in 1..3 %}
                <div class="swiper-slide slide-container">
                    {{ component('placeholders/banner-placeholder',{
                        placeholder_classes: {
                            svg_class: 'slider-image',
                        }})
                    }}
                </div>
            {% endfor %}
        </div>
        <div class="js-swiper-empty-home-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-lg"} %}</div>
        <div class="js-swiper-empty-home-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-lg"} %}</div>
        {% if not params.preview %}
            <div class="placeholder-overlay placeholder-slider transition-soft">
                <div class="placeholder-info">
                    {% include "snipplets/svg/edit.tpl" with {svg_custom_class: "icon-inline icon-3x"} %}
                    <div class="placeholder-description font-small-xs">
                        {{ "Podés subir imágenes principales desde" | translate }} <strong>"{{ "Carrusel de imágenes" | translate }}"</strong>
                    </div>
                    <a href="{{ admin_link }}#instatheme=pagina-de-inicio" class="btn-primary btn btn-small">{{ "Editar" | translate }}</a>
                </div>
            </div>
        {% endif %}
    </div>
</div>

Luego, un segundo snipplet con el nombre featured_products_help.tpl

{# Products featured that work as examples #}

<section class="section-featured-home" data-store="home-products-featured">
    <div class="container position-relative">
        <div class="row">
            <div class="col-12 text-center">
                <h3>{{ 'Productos destacados' | translate }}</h3>
            </div>
            <div class="col-12">
                <div class="js-swiper-featured-demo swiper-container">
                    <div class="swiper-wrapper">
                        {% include 'snipplets/defaults/help_item.tpl' with {'help_item_1': true, 'slide_item': true} %}
                        {% include 'snipplets/defaults/help_item.tpl' with {'help_item_2': true, 'slide_item': true} %}
                        {% include 'snipplets/defaults/help_item.tpl' with {'help_item_3': true, 'slide_item': true} %}
                        {% include 'snipplets/defaults/help_item.tpl' with {'help_item_4': true, 'slide_item': true} %}
                        {% include 'snipplets/defaults/help_item.tpl' with {'help_item_5': true, 'slide_item': true} %}
                        {% include 'snipplets/defaults/help_item.tpl' with {'help_item_6': true, 'slide_item': true} %}
                    </div>
                    <div class="js-swiper-featured-demo-pagination swiper-pagination"></div>
                    <div class="js-swiper-featured-demo-prev swiper-button-prev d-none d-md-block">{% include "snipplets/svg/chevron-left.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div>
                    <div class="js-swiper-featured-demo-next swiper-button-next d-none d-md-block">{% include "snipplets/svg/chevron-right.tpl" with {svg_custom_class: "icon-inline icon-w-8 icon-2x svg-icon-text"} %}</div>
                </div>
            </div>
        </div>
    </div>
</section>

Un tercer snipplet con el nombre banners_help.tpl

{# Main banners that work as examples #}

<section class="section-banners-home position-relative" data-store="{{ data_store }}">
    <div class="container">
        <div class="row align-items-center">
            {% for i in 1..banners_amount %}
                <div class="col-md">
                    <div class="textbanner">
                        <div class="textbanner-image overlay">
                            {{ component('placeholders/banner-placeholder',{
                                placeholder_classes: {
                                    svg_class: 'textbanner-image-background',
                                }})
                            }}
                        </div>
                        <div class="textbanner-text over-image">
                            <div class="h1 textbanner-title">{{ banner_title }}</div>
                        </div>
                    </div>
                </div>
            {% endfor %}
        </div>
    </div>
    {% if not params.preview %}
        <div class="placeholder-overlay transition-soft">
            <div class="placeholder-info">
                {% include "snipplets/svg/edit.tpl" with {svg_custom_class: "icon-inline icon-3x"} %}
                <div class="placeholder-description">
                    {{ banner_help_text }} <strong>"{{ banner_help_section }}"</strong>
                </div>
                <a href="{{ admin_link }}#instatheme=pagina-de-inicio" class="btn-primary btn btn-small">{{ "Editar" | translate }}</a>
            </div>
        </div>
    {% endif %}
</section>

4. Vamos a crear la carpeta svg dentro de la carpeta snipplets

Dentro de la carpeta creada, agregamos el snipplet edit.tpl

<svg class="{{ svg_custom_class }}" viewBox="0 0 576 512"><path d="M417.8 315.5l20-20c3.8-3.8 10.2-1.1 10.2 4.2V464c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V112c0-26.5 21.5-48 48-48h292.3c5.3 0 8 6.5 4.2 10.2l-20 20c-1.1 1.1-2.7 1.8-4.2 1.8H48c-8.8 0-16 7.2-16 16v352c0 8.8 7.2 16 16 16h352c8.8 0 16-7.2 16-16V319.7c0-1.6.6-3.1 1.8-4.2zm145.9-191.2L251.2 436.8l-99.9 11.1c-13.4 1.5-24.7-9.8-23.2-23.2l11.1-99.9L451.7 12.3c16.4-16.4 43-16.4 59.4 0l52.6 52.6c16.4 16.4 16.4 43 0 59.4zm-93.6 48.4L403.4 106 169.8 339.5l-8.3 75.1 75.1-8.3 233.5-233.6zm71-85.2l-52.6-52.6c-3.8-3.8-10.2-4-14.1 0L426 83.3l66.7 66.7 48.4-48.4c3.9-3.8 3.9-10.2 0-14.1z"/></svg>

5. Vamos a buscar el template de la página inicial en templates/home.tpl y usar el siguiente código:

{# Detect presence of features that remove empty placeholders #}
{% set has_main_slider = settings.slider and settings.slider is not empty %}
{% set has_mobile_slider = settings.toggle_slider_mobile and settings.slider_mobile and settings.slider_mobile is not empty %}

{% set show_help = not (has_main_slider or has_mobile_slider) and not has_products %}

{# Detects the design customization page #}

{% set show_component_help = params.preview %}

{% if not params.preview %}
    {% set admin_link = is_theme_draft ? '/admin/themes/settings/draft/' : '/admin/themes/settings/active/' %}
{% endif %}

{#  **** Features Order ****  #}
{% set newArray = [] %}

<div class="js-home-sections-container">
    {# Loops through the number of sections displayed on the home page #}
    {% for i in 1..3 %}
        {% set section = 'home_order_position_' ~ i %}
        {% set section_select = attribute(settings, section) %}

        {% if section_select not in newArray %}
            {% include 'snipplets/home/home-section-switch.tpl' %}
            {% set newArray = newArray|merge([section_select]) %}
        {% endif %}

    {% endfor %}

    {#  **** Hidden Sections ****  #}
    {% if show_component_help %}
        <div style="display:none">
            {# Loops through the names of sections included in settings.txt #}
            {% for section_select in ['slider', 'products', 'categories'] %}
                {% if section_select not in newArray %}
                    {% include 'snipplets/home/home-section-switch.tpl' %}
                {% endif %}
            {% endfor %}
        </div>
    {% endif %}
</div>

{% if settings.home_promotional_popup %}
    {% include 'snipplets/home/home-popup.tpl' %}
{% endif %}


CSS

Requisito:

Tener agregados en tu diseño las clases helpers. Podés seguir este este pequeño tutorial para hacerlo (simplemente es copiar y pegar algunas clases, no toma más de 1 minuto).

1. Agregar los estilos dentro del archivo static/style-async.tpl

Si en tu diseño no usas una hoja de estilos para el CSS asincrónico, vamos a necesitar el siguiente código dentro de tu CSS principal.

{# /* // Placeholders */ #}

.placeholder-overlay {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 9;
  width: 100%;
  height: 100%;  
}

.placeholder-info {
  position: relative;
  top: 50%;
  left: 50%;
  width: 330px;
  padding: 30px 25px;
  text-align: center;
  line-height: 18px;
  transform: translate(-50%, -50%);
  box-sizing: border-box;
  .placeholder-description {
    margin: 20px 0;
  }
}

2. Agregamos el siguiente SASS de colores en style-colors.scss.tpl (o la hoja de tu diseño que tenga los colores y tipografías de la tienda). Recordá que las variables de colores y tipografías pueden variar respecto a tu diseño: 

{# /* // Placeholders */ #}

.placeholder-overlay {
  background-color:rgba($main-foreground, 0.3);
  opacity: 0;
  &:hover,
  &:active,
  &:focus {
    opacity: 1;
  }
}

.placeholder-info {
  color: $main-foreground;
  fill: $main-foreground;
  background-color: $main-background;
  box-shadow: 0 1px 3px rgba(0,0,0,0.5);
}

JS

1. El JavaScript necesitamos agregarlo en el archivo store.js.tpl (o donde tengas tus funciones de JS). El código que necesitamos es el siguiente:

    {#/*============================================================================
      #Sliders
    ==============================================================================*/ #}

    {% if template == 'home' %}

        {# /* // Home slider */ #}

        var width = window.innerWidth;
        if (width > 767) {  
            var slider_autoplay = {delay: 6000,};
        } else {
            var slider_autoplay = false;
        }

        window.homeSlider = {
            getAutoRotation: function() {
                return slider_autoplay;
            },
            updateSlides: function(slides) {
                homeSwiper.removeAllSlides();
                slides.forEach(function(aSlide){
                    homeSwiper.appendSlide(
                        '<div class="swiper-slide slide-container">' +
                            (aSlide.link ? '<a href="' + aSlide.link + '">' : '' ) +
                                '<img src="' + aSlide.src + '" class="slider-image"/>' +
                                '<div class="swiper-text swiper-' + aSlide.color + '">' +
                                    (aSlide.title ? '<div class="swiper-title">' + aSlide.title + '</div>' : '' ) +
                                    (aSlide.description ? '<div class="swiper-description mb-3">' + aSlide.description + '</div>' : '' ) +
                                    (aSlide.button && aSlide.link ? '<div class="btn btn-primary d-inline-block mt-3">' + aSlide.button + '</div>' : '' ) +
                                '</div>' +
                            (aSlide.link ? '</a>' : '' ) +
                        '</div>'
                    );
                });
                if(!slides.length){
                    jQueryNuvem(".js-home-main-slider-container").addClass("hidden");
                    jQueryNuvem(".js-home-empty-slider-container").removeClass("hidden");
                    jQueryNuvem(".js-home-mobile-slider-visibility").removeClass("d-md-none");
                    {% if has_mobile_slider %}
                        jQueryNuvem(".js-home-main-slider-visibility").removeClass("d-none d-md-block");
                        homeMobileSwiper.update();
                    {% endif %}
                }else{
                    jQueryNuvem(".js-home-main-slider-container").removeClass("hidden");
                    jQueryNuvem(".js-home-empty-slider-container").addClass("hidden");
                    jQueryNuvem(".js-home-mobile-slider-visibility").addClass("d-md-none");
                    {% if has_mobile_slider %}
                        jQueryNuvem(".js-home-main-slider-visibility").addClass("d-none d-md-block");
                    {% endif %}
                }
            },
        };

        var preloadImagesValue = false;
        var lazyValue = true;
        var loopValue = true;
        var watchOverflowValue = true;
        var paginationClickableValue = true;

        var homeSwiper = null;
        createSwiper(
            '.js-home-slider',
            {
                lazy: lazyValue,
                preloadImages: preloadImagesValue,
                {% if settings.slider | length > 1 %}
                    loop: loopValue,
                {% endif %}
                autoplay: slider_autoplay,
                watchOverflow: watchOverflowValue,
                pagination: {
                    el: '.js-swiper-home-pagination',
                    clickable: paginationClickableValue,
                },
                navigation: {
                    nextEl: '.js-swiper-home-next',
                    prevEl: '.js-swiper-home-prev',
                },
            },
            function(swiperInstance) {
                homeSwiper = swiperInstance;
            }
        );

        var homeMobileSwiper = null;
        createSwiper(
            '.js-home-slider-mobile',
            {
                lazy: lazyValue,
                preloadImages: preloadImagesValue,
                {% if settings.slider_mobile | length > 1 %}
                    loop: loopValue,
                {% endif %}
                autoplay: slider_autoplay,
                watchOverflow: watchOverflowValue,
                pagination: {
                    el: '.js-swiper-home-pagination-mobile',
                    clickable: paginationClickableValue,
                },
                navigation: {
                    nextEl: '.js-swiper-home-next-mobile',
                    prevEl: '.js-swiper-home-prev-mobile',
                },
            },
            function(swiperInstance) {
                homeMobileSwiper = swiperInstance;
            }
        );
     
        {% if sections.primary.products %}

            {% set columns = settings.grid_columns %}
            createSwiper('.js-swiper-featured', {
                lazy: true,
                loop: true,
                spaceBetween: 30,
                threshold: 5,
                watchSlidesVisibility: true,
                slideVisibleClass: 'js-swiper-slide-visible',
                slidesPerView: {% if columns == 2 %}2{% else %}1{% endif %},
                pagination: {
                    el: '.js-swiper-featured-pagination',
                    clickable: true,
                },
                navigation: {
                    nextEl: '.js-swiper-featured-next',
                    prevEl: '.js-swiper-featured-prev',
                },
                breakpoints: {
                    640: {
                        slidesPerView: {% if columns == 2 %}4{% else %}3{% endif %},
                    }
                },
            });

        {% endif %}

    {% endif %}

    {#/*============================================================================
      #Empty placeholders
    ==============================================================================*/ #}

    {% if template == 'home' %}

        {# /* // Home demo slider */ #}

        var width = window.innerWidth;
        if (width > 767) {  
            var slider_empty_autoplay = {delay: 6000,};
        } else {
            var slider_empty_autoplay = false;
        }

        window.homeEmptySlider = {
            getAutoRotation: function() {
                return slider_empty_autoplay;
            },
        };
        createSwiper('.js-home-empty-slider', {
            loop: true,
            autoplay: slider_empty_autoplay,
            pagination: {
                el: '.js-swiper-empty-home-pagination',
                clickable: true,
            },
            navigation: {
                nextEl: '.js-swiper-empty-home-next',
                prevEl: '.js-swiper-empty-home-prev',
            },
        });

        {# /* // Home demo products slider */ #}

        {% set columns = settings.grid_columns %}
        createSwiper('.js-swiper-featured-demo', {
            slidesPerView: {% if columns == 2 %}2{% else %}1{% endif %},
            spaceBetween: 30,
            navigation: {
                nextEl: '.js-swiper-featured-demo-next',
                prevEl: '.js-swiper-featured-demo-prev',
            },
            pagination: {
                el: '.js-swiper-featured-demo-pagination',
                clickable: true,
            },
            breakpoints: {
                640: {
                    slidesPerView: {% if columns == 2 %}4{% else %}3{% endif %},
                }
            },
        });
    {% endif %}


2. Dentro de la carpeta static/js debemos agregar el archivo instatheme.js. Cada sección de la página de inicio debería tener un "data-store" para identificarla y asociarla al componente.

window.tiendaNubeInstaTheme = (function(jQueryNuvem) {
    return {
        waitFor: function() {
            return [window.homeSlider];
        },
        handlers: function(instaElements) {
            return {
                logo: new instaElements.Logo({
                    $storeName: jQueryNuvem('#no-logo'),
                    $logo: jQueryNuvem('#logo')
                }),
                // ----- Section order -----
                home_order_position: new instaElements.Sections({
                    container: '.js-home-sections-container',
                    data_store: {
                        'slider': 'home-slider',
                        'products': 'home-products-featured',
                        'categories': 'home-banner-categories',
                    }
                }),
                slider: new instaElements.GenericSlider(window.homeSlider),
                slider_auto: new instaElements.AutoSliderCheckbox({
                    slider: 'slider'
                })
            };
        }
    };
})(jQueryNuvem);

Configuraciones

En el archivo config/settings.txt vamos a agregar el siguiente código.

Se agrega un "default = home_order_position" dentro de la sección Pagina de inicio. A su vez, para cada collapse de la sección un "backto = home_order_position" y al final dentro de "section_order" se listan las secciones.

Página de inicio
    meta
        icon = home
        advanced = true
        default = home_order_position
    collapse
        title = Carrusel de imágenes
        backto = home_order_position
    subtitle
        subtitle = Imágenes para computadoras
    gallery
        name = slider
        gallery_image = Agregar imagen
        gallery_link = [Opcional] Página web a la que quieres que te lleve la imagen al hacer click
        gallery_width = 1580
        gallery_more_info = true
    subtitle
        subtitle = Imágenes para celulares
    description
        description = Mejorá la calidad y velocidad de carga con esta opción
    checkbox
        name = toggle_slider_mobile
        description = Cargar otras imágenes para celulares
    gallery
        name = slider_mobile
        gallery_image = Agregar imagen
        gallery_link = [Opcional] Página web a la que quieres que te lleve la imagen al hacer click
        gallery_width = 820
        gallery_height = 1460
        gallery_more_info = true
    subtitle
        subtitle = <a target='_blank' data-toggle-info='show-on_slider_mobile-inactive' href='https://ayuda.tiendanube.com/es/articles/3297783-como-usar-el-carrusel-de-imagenes-para-las-plantillas-idea-bahia-y-amazonas'>¿Cómo diseñar las imágenes para tu carrusel?</a>
    collapse
        title = Banners de categorías
        backto = home_order_position
    description
        description = Los banners se mostrarán de izquierda a derecha en computadoras y de arriba hacia abajo en celulares
    checkbox
        name = banners_full
        description = Mostrar banner a todo el ancho de la pantalla en computadoras
    subtitle
        subtitle = CATEGORÍA 1
    checkbox
        name = banner_01_show
        description = Mostrar banner
    image
        original = banner_01.jpg
        title = Cargar imagen (JPG, GIF, PNG)
        width = 600
        height = 600
    i18n_input
        name = banner_01_title
        description = Título
    i18n_input
        name = banner_01_description
        description = Descripción
    i18n_input
        name = banner_01_button
        description = Texto del botón
    i18n_input
        name = banner_01_url
        description = Link
    subtitle
        subtitle = CATEGORÍA 2
    checkbox
        name = banner_02_show
        description = Mostrar banner
    image
        original = banner_02.jpg
        title = Cargar imagen (JPG, GIF, PNG)
        width = 600
        height = 600
    i18n_input
        name = banner_02_title
        description = Título
    i18n_input
        name = banner_02_description
        description = Descripción
    i18n_input
        name = banner_02_button
        description = Texto del botón
    i18n_input
        name = banner_02_url
        description = Link
    collapse
        title = Productos destacados
        backto = home_order_position
    description
        description = Podés destacar los productos que quieras en la página de inicio.
        name = featured_products_description
    subtitle
        subtitle = <a target='_blank' href='/admin/themes/settings/product_order/'>Elegí cuáles vas a destacar</a>
    collapse
        title = Pop-up promocional
        backto = home_order_position
    checkbox
        name = home_promotional_popup
        description = Mostrar pop-up
    subtitle
        subtitle = Imagen para el pop-up
    image
        original = home_popup_image.jpg
        title = Cargar imagen (JPG, GIF, PNG)
    subtitle
        subtitle = Frase motivadora para el pop-up
    i18n_input
        name = home_popup_txt
    i18n_input
        name = home_popup_url
        description = Link (Opcional)
    subtitle
        subtitle = Newsletter
    checkbox
        name = home_news_box
        description = Permitir que tus clientes se registren para recibir novedades desde el pop-up (Opcional)
    collapse
        title = Página de inicio
        backto = _top
    section_order
        name = home_order_position
        start_index = 1
        sections
            slider = Carrusel de imágenes
            products = Productos destacados
            categories = Banners de categorías
    link
        text = Pop-up promocional
        linkto = home_promotional_popup

Traducciones

En este paso agregamos los textos para las traducciones en el archivo config/translations.txt

--- --- Placeholders

es "Carrusel de imágenes"
pt "Banners rotativos"
en "Slider"
es_mx "Carrusel de imágenes"

es "Categoría"
pt "Categoria"
en "Category"
es_mx "Categoría"

es "Banners de categorías"
pt "Banners de categorias"
en "Categories banners"
es_mx "Banners de categorías"

es "Podés subir imágenes principales desde"
pt "Você pode subir as imagens principais desde"
en "You can upload main images from"
es_mx "Puedes subir imágenes principales desde"

es "Podés destacar categorías de tu tienda desde"
pt "Você pode destacar categorias da sua loja desde"
en "You can highlight a categories of your store from"
es_mx "Puedes destacar categorías de tu tienda desde"

es "Editar"
pt "Editar"
en "Edit"
es_mx "Editar"

es "Productos destacados"
pt "Produtos em destaque"
en "Featured products"
es_mx "Productos destacados"

es "Producto de ejemplo"
pt "Produto de exemplo"
en "Product example"
es_mx "Producto de ejemplo"