From 3ae3fe87d37db1bc8776997bf62ec7caa10d29fc Mon Sep 17 00:00:00 2001 From: tblivet Date: Thu, 28 Aug 2025 14:43:36 +0200 Subject: [PATCH 1/7] fix: modal update duplication --- templates/catalog/_partials/product-cover-thumbnails.tpl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/templates/catalog/_partials/product-cover-thumbnails.tpl b/templates/catalog/_partials/product-cover-thumbnails.tpl index fa0d9a324..690913c53 100644 --- a/templates/catalog/_partials/product-cover-thumbnails.tpl +++ b/templates/catalog/_partials/product-cover-thumbnails.tpl @@ -165,8 +165,9 @@ > {/if} + + {block name='product_images_modal'} + {include file='catalog/_partials/product-images-modal.tpl'} + {/block} -{block name='product_images_modal'} - {include file='catalog/_partials/product-images-modal.tpl'} -{/block} From 80e4a23bd78e96d064747c515c2764944b859b6c Mon Sep 17 00:00:00 2001 From: tblivet Date: Thu, 28 Aug 2025 15:38:41 +0200 Subject: [PATCH 2/7] fix: zoom button duplicate --- templates/catalog/_partials/product-cover-thumbnails.tpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/catalog/_partials/product-cover-thumbnails.tpl b/templates/catalog/_partials/product-cover-thumbnails.tpl index 690913c53..3a6369d02 100644 --- a/templates/catalog/_partials/product-cover-thumbnails.tpl +++ b/templates/catalog/_partials/product-cover-thumbnails.tpl @@ -67,12 +67,12 @@ data-full-size-image-url="{$image.bySize.home_default.url}" > - -
- -
{/foreach} + + {/block} From 9a5936c2963c55a672cb81209db7340b997a04b5 Mon Sep 17 00:00:00 2001 From: tblivet Date: Thu, 28 Aug 2025 18:22:54 +0200 Subject: [PATCH 3/7] feat: accessibility improvements --- src/js/constants/selectors-map.ts | 2 + src/js/product.ts | 44 ++++++++++++++ src/scss/prestashop/pages/_product.scss | 11 +++- .../catalog/_partials/product-add-to-cart.tpl | 2 +- .../_partials/product-cover-thumbnails.tpl | 57 +++++++++---------- .../_partials/product-images-modal.tpl | 28 ++++----- 6 files changed, 97 insertions(+), 47 deletions(-) diff --git a/src/js/constants/selectors-map.ts b/src/js/constants/selectors-map.ts index 7ce1bd392..f4cb6ab6d 100644 --- a/src/js/constants/selectors-map.ts +++ b/src/js/constants/selectors-map.ts @@ -179,6 +179,8 @@ const selectorsMap = { carousel: '.js-product-carousel', miniature: '.js-product-miniature', thumbnail: '.js-thumb-container', + productImagesModal: '[data-ps-ref="product-images-modal"]', + productImagesModalCarousel: '[data-ps-ref="product-images-modal-carousel"]', activeThumbail: (id: number): string => `.js-thumb-container:nth-child(${id + 1})`, }, order: { diff --git a/src/js/product.ts b/src/js/product.ts index a73e0c6f5..6d0fa5050 100644 --- a/src/js/product.ts +++ b/src/js/product.ts @@ -3,6 +3,7 @@ * file that was distributed with this source code. */ +import {Carousel} from 'bootstrap'; import SelectorsMap from './constants/selectors-map'; type ProductSlideEvent = Event & {to: number}; @@ -70,4 +71,47 @@ export default () => { // Call the function to start listening for quantity changes detectQuantityChange(); + + // Product images sync on open/close modal + document.addEventListener('show.bs.modal', (event) => { + const modal = event.target as HTMLElement; + + if (!modal.matches(SelectorsMap.product.productImagesModal)) return; + + const modalCarouselElement = modal.querySelector(SelectorsMap.product.productImagesModalCarousel); + const mainCarouselElement = document.querySelector(SelectorsMap.product.carousel); + + if (!modalCarouselElement || !mainCarouselElement) return; + + const modalCarousel = Carousel.getOrCreateInstance(modalCarouselElement as HTMLElement); + const activeIndex = getActiveSlideIndex(mainCarouselElement as HTMLElement); + + if (activeIndex !== -1) { + modalCarousel.to(activeIndex); + } + }); + + document.addEventListener('hide.bs.modal', (event) => { + const modal = event.target as HTMLElement; + + if (!modal.matches(SelectorsMap.product.productImagesModal)) return; + + const modalCarouselElement = modal.querySelector(SelectorsMap.product.productImagesModalCarousel); + const mainCarouselElement = document.querySelector(SelectorsMap.product.carousel); + + if (!modalCarouselElement || !mainCarouselElement) return; + + const mainCarousel = Carousel.getOrCreateInstance(mainCarouselElement as HTMLElement); + const activeIndex = getActiveSlideIndex(modalCarouselElement as HTMLElement); + + if (activeIndex !== -1) { + mainCarousel.to(activeIndex); + } + }); + + const getActiveSlideIndex = (carouselEl: HTMLElement): number => { + const items = carouselEl.querySelectorAll('.carousel-item'); + + return Array.from(items).findIndex((item) => item.classList.contains('active')); + }; }; diff --git a/src/scss/prestashop/pages/_product.scss b/src/scss/prestashop/pages/_product.scss index 74da8846a..9555412aa 100644 --- a/src/scss/prestashop/pages/_product.scss +++ b/src/scss/prestashop/pages/_product.scss @@ -16,13 +16,20 @@ $component-name: product; } &__thumbnail { + padding: 0; + background-color: transparent; + border: none; + border-radius: var(--bs-border-radius); + &.active .#{$component-name}__thumbnail-image { - outline: 2px solid var(--bs-primary); + outline: 0.125rem solid var(--bs-primary); + outline-offset: -0.125rem; } &-image { border-radius: var(--bs-border-radius); - outline: 2px solid transparent; + outline: 0.125rem solid transparent; + outline-offset: -0.125rem; } } diff --git a/templates/catalog/_partials/product-add-to-cart.tpl b/templates/catalog/_partials/product-add-to-cart.tpl index b73fdb354..8b0d85004 100644 --- a/templates/catalog/_partials/product-add-to-cart.tpl +++ b/templates/catalog/_partials/product-add-to-cart.tpl @@ -27,7 +27,7 @@ {/if} {** And render the availability message with icon *} -