Product Gallery¶
Use product_gallery
module to insert a product gallery on your product template view. This module is available only within a product card context.
It does provide configurable gallery with thumbnails view.
Configuration parameters¶
hasMobileArrowsDisplayEnabled¶
int
if set to 1 the view of navigation arrows for thumbnail gallery below 768px will be enabled.
hasDesktopArrowsDisplayEnabled¶
int
if set to 1 the view of navigation arrows for thumbnail gallery above 768px will be enabled.
thumbnailsPosition¶
left | right | bottom
Sets where the thumbnails gallery is displayed. On the left side of main photo, right side of main photo or below main photo.
displayPhotoDescription¶
int
if set to 1 the description or the name of the image file will be rendered.
Module source code¶
{% from "@macros/image.twig" import image %}
{% from "@macros/slider_arrow_left.twig" import slider_arrow_left %}
{% from "@macros/slider_arrow_right.twig" import slider_arrow_right %}
{% from "@macros/icon.twig" import icon %}
{% set product = ObjectApi.getProduct(product_id) %}
{% set shopUrls = ObjectApi.getShopUrls() %}
{% set availability = product.availability %}
{% set isInactive = not availability.isAvailable and availability.visibilityConfig.isProductPageGalleryAndNameGrey %}
{% set inactiveMainImageClass = isInactive ? 'product-gallery__main-photo_inactive inactive' : '' %}
{% set inactiveThumbnailClass = isInactive ? 'product-gallery__thumbnail-item_inactive inactive' : '' %}
{% set featuredImage = product.featuredImage %}
{% set isThumbnailsPositionUnder = moduleConfig.thumbnailsPosition == 'under' %}
{% set mainPhotoGallerySettings = {
"type": "slide",
"rewind": true,
"pagination": false,
"arrows" :true,
"keyboard": "global",
"lazyLoad": "nearby",
"gap": "0.5rem",
"breakpoints": {
768: {
"padding": { right: moduleConfig.hasMobileArrowsDisplayEnabled ? "" : "2rem" }
}
}
} %}
{% if isThumbnailsPositionUnder %}
{% set thumbnailsGallerySettings = {
"arrows": false,
"fixedWidth": 60,
"fixedHeight": 80,
"perMove": 1,
"pagination": false,
"isNavigation": true,
"gap": "0.5rem",
"focus": 'center'
} %}
{% else %}
{% set thumbnailsGallerySettings = {
"arrows": false,
"fixedWidth": 60,
"fixedHeight": 80,
"direction": 'ttb',
"autoHeight": true,
"height": 'auto',
"pagination": false,
"isNavigation": true,
"lazyLoad": true,
"focus": 'center'
} %}
{% endif %}
<product-gallery
on-interaction
main="product-main-photo-{{ moduleInstance }}"
thumbnails="product-thumbnails-{{ moduleInstance }}"
class="product-gallery{% if isThumbnailsPositionUnder %} product-gallery_vertical{% endif %}"
>
<h-slider
id="product-main-photo-{{ moduleInstance }}"
class="splide product-gallery__slider product-gallery__slider_main-image"
settings="{{ mainPhotoGallerySettings | json_encode }}">
<div class="splide__track">
<ul class="splide__list">
{{ product.images.setItemCountPerPage(product.images.count) }}
{% for image in product.images %}
<li class="splide__slide{% if not image.isFeatured %} splide__slide_hide{% endif %}">
<a class="js__gallery-anchor-image" href="{{ image.webpUrl }}">
{% set imgProperties = {
src: image.webpThumbnailUrl(systemConfig.xlSize, systemConfig.xlSize),
class: "js__open-gallery product-gallery__main-image #{inactiveMainImageClass}",
alt: image.name,
title: image.name,
width: systemConfig.xlSize,
height: systemConfig.xlSize,
sizes: "(min-width: 576px) #{systemConfig.xlSize}px, 100vw",
"data-gallery-name-to-open": "productGallery",
"data-gallery-name": "productGallery",
"data-image-description": moduleConfig.displayPhotoDescription ? image.name : '',
} %}
{% if image.isFeatured %}
{% set imgProperties = imgProperties|merge({ 'srcset': image.webpThumbnailUrl(150,150) ~' 150w, ' ~ image.webpThumbnailUrl(500,500) ~ ' 500w, ' ~ image.webpUrl ~ ' 800w', 'fetchpriority': 'high', 'decoding': 'sync'}) %}
{% else %}
{% set imgProperties = imgProperties|merge({ 'data-splide-lazy-srcset': image.webpThumbnailUrl(150,150) ~' 150w, ' ~ image.webpThumbnailUrl(500,500) ~ ' 500w, ' ~ image.webpUrl ~ ' 800w', decoding: 'async', loading: 'lazy' }) %}
{% endif %}
{{ image({ img: imgProperties }, [
{
src: image.webpThumbnailUrl(systemConfig.xlSize, systemConfig.xlSize),
type: 'image/webp'
}
]) }}
</a>
</li>
{% endfor %}
</ul>
</div>
<div class="splide__arrows" hidden>
{{ slider_arrow_left({
alignment: 'pagination',
title: translate('Previous image'),
class: (moduleConfig.hasMobileArrowsDisplayEnabled ? '' : ' hidden-xs-only hidden-sm-only') ~ (moduleConfig.hasDesktopArrowsDisplayEnabled ? '' : ' hidden-md-only hidden-lg-only hidden-xl-only hidden-xxl-only hidden-xxxl-only')
}) }}
{{ slider_arrow_right({
alignment: 'pagination',
title: translate('Next image'),
class: (moduleConfig.hasMobileArrowsDisplayEnabled ? '' : ' hidden-xs-only hidden-sm-only') ~ (moduleConfig.hasDesktopArrowsDisplayEnabled ? '' : ' hidden-md-only hidden-lg-only hidden-xl-only hidden-xxl-only hidden-xxxl-only')
}) }}
</div>
</h-slider>
{% if product.images.count > 1 %}
<h-slider
id="product-thumbnails-{{ moduleInstance }}"
class="splide splide_non-floating-arrows-{% if isThumbnailsPositionUnder %}horizontal{% else %}vertical{% endif %} hidden-xs-only product-gallery__slider product-gallery__slider_thumbnails product-gallery__slider_thumbnails-{{ moduleConfig.thumbnailsPosition }}"
settings="{{ thumbnailsGallerySettings | json_encode }}">
{% if isThumbnailsPositionUnder %}
<div class="product-gallery__thumbnail-arrow product-gallery__thumbnail-arrow_left js__splide-move-by-prev" hidden>
<button class="slider__arrow_secondary " >
{{ icon('icon-chevron-left', {
size: 'm',
title: 'todo: title'
}) }}
</button>
</div>
{% else %}
<div class="product-gallery__thumbnail-arrow product-gallery__thumbnail-arrow_up js__splide-move-by-prev" hidden>
<button class="slider__arrow_secondary">
{{ icon('icon-chevron-up', {
size: 'm',
title: 'todo: title'
}) }}
</button>
</div>
{% endif %}
<div class="splide__track {% if isThumbnailsPositionUnder %} splide__track_horizontal {% endif %}">
<ul class="splide__list {% if isThumbnailsPositionUnder %} splide__list_horizontal {% endif %}">
{% for image in product.images %}
<li class="splide__slide">
<div class="product-gallery__thumbnail-item">
{{ image({
img: {
src: image.thumbnailUrl(systemConfig.xsSize, systemConfig.xsSize),
alt: image.name,
title: image.name,
width: systemConfig.xsSize,
height: systemConfig.xsSize,
decoding: 'async',
loading: 'lazy',
class: inactiveThumbnailClass
}
}, [
{
src: image.webpThumbnailUrl(systemConfig.xsSize, systemConfig.xsSize),
type: 'image/webp'
}
]) }}
</div>
</li>
{% endfor %}
</ul>
</div>
{% if isThumbnailsPositionUnder %}
<div class="product-gallery__thumbnail-arrow product-gallery__thumbnail-arrow_right js__splide-move-by-next" hidden>
<button class="slider__arrow_secondary">
{{ icon('icon-chevron-right', {
size: 'm',
title: 'todo: title'
}) }}
</button>
</div>
{% else %}
<div class="product-gallery__thumbnail-arrow product-gallery__thumbnail-arrow_down js__splide-move-by-next" hidden>
<button class="slider__arrow_secondary">
{{ icon('icon-chevron-down', {
size: 'm',
title: 'todo: title'
}) }}
</button>
</div>
{% endif %}
</h-slider>
{% endif %}
</product-gallery>
{% set productImages = []|merge(product.images
| filter(image => image.isPlaceholder == false)
| sort((a,b) => b.isFeatured - a.isFeatured)
| map(image => "#{image.url.absolute}")) %}
{% set productGalleryJsonLd = {
"@context": [
"http://schema.org/",
{ "@base": "#{shopUrls.mainPageUrl.absolute}" }
],
"@id": "#{product.url.relative}",
"image": productImages
} %}
<script type="application/ld+json">{{ productGalleryJsonLd | json_encode | raw }}</script>
The product gallery consists of two parts which are h-slider
webcomponents. Each differently configured but both synced with each other with help of product-gallery
webcomponent that serves as a parent for them.
As seen in source code above there is a lot of variables that may be changed, resulting in different gallery behaviour. Both galleries (main and thumbnail) use h-slider webcomponent where You can read about possible configuration.
The module uses JSON-LD and Microdata from schema.org to optimize search results in browsers.
Webcomponents reference¶
Macros reference¶
Used Object Api methods¶
Used styles¶
Module configuration schema¶
[
{
"state": "unfolded",
"label": "Gallery main view",
"elements": [
{
"type": "header",
"name": "header_mobile_devices",
"label": "Mobile devices",
"options": {
"icon": "mobile_devices"
},
"children": [
{
"type": "checkbox",
"name": "hasMobileArrowsDisplayEnabled",
"label": "Enable navigation arrows",
"defaultValue": 1
}
]
},
{
"type": "header",
"name": "header_computers",
"label": "Computers",
"options": {
"icon": "computers"
},
"children": [
{
"type": "checkbox",
"name": "hasDesktopArrowsDisplayEnabled",
"label": "Enable navigation arrows",
"defaultValue": 1
},
{
"type": "radio",
"name": "thumbnailsPosition",
"label": "Thumbnail display position",
"options": {
"radioOptions": [
{
"key": "left",
"label": "On the left of the photo"
},
{
"key": "right",
"label": "On the right of the photo"
},
{
"key": "under",
"label": "Under the photo"
}
]
}
}
]
}
]
},
{
"state": "unfolded",
"label": "Full screen view",
"elements": [
{
"type": "infobox",
"name": "infobox",
"options": {
"type": "warning",
"message": "Changes to the settings in this section will not be visible in the live view. To see the changes, save them, click \"Preview\" and click on the photo in the gallery."
}
},
{
"type": "checkbox",
"name": "displayPhotoDescription",
"label": "Display photo description \/ file name",
"defaultValue": 1
}
]
}
]