diff --git a/assets/css/theme-layer2-attributes.css b/assets/css/theme-layer2-attributes.css index 7ea4ff9..f1137b8 100644 --- a/assets/css/theme-layer2-attributes.css +++ b/assets/css/theme-layer2-attributes.css @@ -435,6 +435,112 @@ transition: transform 0.2s ease; } +/* PDP Gallery — mobile: swipe + dots, desktop: carousel + thumbs */ +.pdp-gallery-carousel, +.pdp-gallery-single { + aspect-ratio: 1 / 1; + background-color: #e5e7eb; + overflow: hidden; +} + +.pdp-gallery-single { + position: relative; +} + +.pdp-gallery-carousel { + display: flex; + overflow-x: auto; + scroll-snap-type: x mandatory; + scroll-behavior: smooth; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; + + &::-webkit-scrollbar { + display: none; + } +} + +.pdp-carousel-img { + flex: 0 0 100%; + width: 100%; + height: 100%; + object-fit: cover; + scroll-snap-align: start; +} + +/* Desktop-only: lightbox click target + nav arrows (hidden on mobile) */ +.pdp-lightbox-click, +.pdp-nav { + display: none; +} + +.pdp-gallery-thumbs { + display: none; +} + +@media (hover: hover) { + .pdp-lightbox-click { + display: block; + position: absolute; + inset: 0; + z-index: 1; + cursor: zoom-in; + } + + .pdp-nav { + display: flex; + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 2; + align-items: center; + justify-content: center; + width: 2.5rem; + height: 2.5rem; + border-radius: 9999px; + background: rgba(255, 255, 255, 0.9); + color: #374151; + border: none; + cursor: pointer; + opacity: 0; + transition: opacity 0.15s ease; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); + + & svg { + width: 1.25rem; + height: 1.25rem; + } + } + + .pdp-nav-prev { + left: 0.75rem; + } + + .pdp-nav-next { + right: 0.75rem; + } + + /* Show arrows on gallery hover */ + .pdp-gallery:hover .pdp-nav { + opacity: 1; + } + + .pdp-nav:hover { + background: rgba(255, 255, 255, 1); + } + + .pdp-gallery-single { + cursor: zoom-in; + } + + .pdp-gallery-thumbs { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1rem; + margin-top: 1rem; + } +} + /* Lightbox */ .lightbox { position: fixed; @@ -572,7 +678,3 @@ font-family: var(--t-font-body); } -/* PDP Main Image zoom cursor */ -.pdp-main-image-container { - cursor: zoom-in; -} diff --git a/assets/js/app.js b/assets/js/app.js index 030ae34..9bfd27c 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -286,15 +286,41 @@ const Lightbox = { const ProductImageScroll = { mounted() { - const dots = this.el.parentElement.querySelector('.product-image-dots') - if (!dots) return - const spans = dots.querySelectorAll('.product-image-dot') + const container = this.el.parentElement + const dots = container.querySelector('.product-image-dots') + const spans = dots ? dots.querySelectorAll('.product-image-dot') : [] + const lightbox = container.parentElement.querySelector('dialog') + const thumbs = container.parentElement.querySelector('.pdp-gallery-thumbs') + const thumbButtons = thumbs ? thumbs.querySelectorAll('.pdp-thumbnail') : [] + const imageCount = this.el.children.length + this.el.addEventListener('scroll', () => { const index = Math.round(this.el.scrollLeft / this.el.offsetWidth) spans.forEach((dot, i) => { dot.classList.toggle('product-image-dot-active', i === index) }) + thumbButtons.forEach((btn, i) => { + btn.classList.toggle('pdp-thumbnail-active', i === index) + }) + if (lightbox) lightbox.dataset.currentIndex = index.toString() }, {passive: true}) + + this.el.addEventListener('pdp:scroll-to', (e) => { + const index = e.detail.index + this.el.scrollTo({left: index * this.el.offsetWidth, behavior: 'smooth'}) + }) + + this.el.addEventListener('pdp:scroll-prev', () => { + const current = Math.round(this.el.scrollLeft / this.el.offsetWidth) + const target = (current - 1 + imageCount) % imageCount + this.el.scrollTo({left: target * this.el.offsetWidth, behavior: 'smooth'}) + }) + + this.el.addEventListener('pdp:scroll-next', () => { + const current = Math.round(this.el.scrollLeft / this.el.offsetWidth) + const target = (current + 1) % imageCount + this.el.scrollTo({left: target * this.el.offsetWidth, behavior: 'smooth'}) + }) } } diff --git a/lib/simpleshop_theme_web/components/shop_components/product.ex b/lib/simpleshop_theme_web/components/shop_components/product.ex index 2b93870..0ed7d2d 100644 --- a/lib/simpleshop_theme_web/components/shop_components/product.ex +++ b/lib/simpleshop_theme_web/components/shop_components/product.ex @@ -1134,52 +1134,136 @@ defmodule SimpleshopThemeWeb.ShopComponents.Product do def product_gallery(assigns) do ~H""" -