Files
3dealer/resources/js/stimulus/product-gallery-controller.js
T
2026-07-31 17:41:55 +03:00

117 lines
3.5 KiB
JavaScript

import { Controller } from "@hotwired/stimulus";
const SCROLL_STEP = 1;
export default class extends Controller {
static targets = ["main", "thumb", "track", "lightbox", "lightboxImage", "lightboxCounter"];
connect() {
this.offset = 0;
this.lightboxIndex = 0;
this.#syncHeight();
this.mainTarget.addEventListener("load", () => this.#syncHeight());
this._onKeydown = this.#handleKeydown.bind(this);
document.addEventListener("keydown", this._onKeydown);
}
disconnect() {
document.removeEventListener("keydown", this._onKeydown);
}
select(event) {
const btn = event.currentTarget;
this.#setThumb(btn);
this.lightboxIndex = this.thumbTargets.indexOf(btn);
}
scrollUp() {
this.offset = Math.max(0, this.offset - SCROLL_STEP);
this.#applyScroll();
}
scrollDown() {
this.offset = Math.min(this.thumbTargets.length - 1, this.offset + SCROLL_STEP);
this.#applyScroll();
}
closeLightboxOnBackdrop(event) {
// Only close if clicking directly on the backdrop (the popover div itself),
// not on the image, arrows, close button, or counter
if (event.target === this.lightboxTarget) {
this.lightboxTarget.hidePopover();
}
}
noop(event) {
event.stopPropagation();
}
openLightbox() {
this.#updateLightbox(this.lightboxIndex);
this.lightboxTarget.showPopover();
}
prevImage() {
const next = (this.lightboxIndex - 1 + this.thumbTargets.length) % this.thumbTargets.length;
this.#updateLightbox(next);
}
nextImage() {
const next = (this.lightboxIndex + 1) % this.thumbTargets.length;
this.#updateLightbox(next);
}
// ── Private ────────────────────────────────────────────────────
#updateLightbox(index) {
this.lightboxIndex = index;
const thumb = this.thumbTargets[index];
if (!thumb) return;
this.lightboxImageTarget.src = thumb.dataset.src;
this.lightboxImageTarget.alt = thumb.dataset.alt;
this.lightboxCounterTarget.textContent =
`${index + 1} of ${this.thumbTargets.length}`;
}
#setThumb(btn) {
const src = btn.dataset.src;
const alt = btn.dataset.alt;
this.mainTarget.src = src;
this.mainTarget.alt = alt;
this.thumbTargets.forEach((t) => {
const active = t === btn;
t.classList.toggle("border-2", active);
t.classList.toggle("border-1", !active);
t.setAttribute("aria-pressed", String(active));
});
}
#applyScroll() {
if (!this.thumbTargets.length) return;
const thumbHeight = this.thumbTargets[0].offsetHeight;
const gap = 16; // gap-4 = 16px
this.trackTarget.scrollTop = this.offset * (thumbHeight + gap);
}
#syncHeight() {
const h = this.mainTarget.offsetHeight;
if (h > 0) {
this.trackTarget.style.maxHeight = h + "px";
}
}
#handleKeydown(e) {
if (!this.hasLightboxTarget) return;
if (!this.lightboxTarget.matches(":popover-open")) return;
if (e.key === "ArrowLeft") { e.preventDefault(); this.prevImage(); }
if (e.key === "ArrowRight") { e.preventDefault(); this.nextImage(); }
if (e.key === "Escape") { this.lightboxTarget.hidePopover(); }
}
}