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(); } } }