generated from boboko/starter
setting up front 1
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
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(); }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user