135 lines
4.7 KiB
JavaScript
135 lines
4.7 KiB
JavaScript
/**
|
|
* Galerie photos avec modal plein écran.
|
|
* Gère : onglets par pièce, navigation clavier, swipe mobile.
|
|
*/
|
|
class Gallery {
|
|
/**
|
|
* @param {Object} data - { 'gallery-key': [{src, caption}, …], … }
|
|
* @param {HTMLElement} modal - élément .gallery-modal
|
|
*/
|
|
constructor(data, modal) {
|
|
this.data = data; // toutes les galeries indexées par clé
|
|
this.currentKey = null; // clé de la galerie active
|
|
this.currentIndex = 0; // index dans la galerie active
|
|
|
|
this.modal = modal;
|
|
this.img = modal.querySelector('#gallery-modal-img');
|
|
this.caption = modal.querySelector('#gallery-modal-caption');
|
|
this.counter = modal.querySelector('#gallery-counter');
|
|
this.overlay = modal.querySelector('#gallery-overlay');
|
|
this.btnClose = modal.querySelector('#gallery-close');
|
|
this.btnPrev = modal.querySelector('#gallery-prev');
|
|
this.btnNext = modal.querySelector('#gallery-next');
|
|
|
|
this._touchStartX = 0;
|
|
|
|
this._bindEvents();
|
|
this._initTabs();
|
|
}
|
|
|
|
// ── Initialisation ─────────────────────────────────────────────────────────
|
|
|
|
_bindEvents() {
|
|
// Boutons de navigation
|
|
this.btnClose.addEventListener('click', () => this.close());
|
|
this.overlay.addEventListener('click', () => this.close());
|
|
this.btnPrev.addEventListener('click', () => this.prev());
|
|
this.btnNext.addEventListener('click', () => this.next());
|
|
|
|
// Clavier
|
|
document.addEventListener('keydown', (e) => {
|
|
if (this.modal.hidden) return;
|
|
if (e.key === 'Escape') this.close();
|
|
if (e.key === 'ArrowLeft') this.prev();
|
|
if (e.key === 'ArrowRight') this.next();
|
|
});
|
|
|
|
// Swipe mobile
|
|
this.modal.addEventListener('touchstart', (e) => {
|
|
this._touchStartX = e.touches[0].clientX;
|
|
}, { passive: true });
|
|
this.modal.addEventListener('touchend', (e) => {
|
|
const dx = e.changedTouches[0].clientX - this._touchStartX;
|
|
if (Math.abs(dx) > 50) {
|
|
dx < 0 ? this.next() : this.prev();
|
|
}
|
|
});
|
|
|
|
// Délégation : clic sur les vignettes
|
|
document.addEventListener('click', (e) => {
|
|
const item = e.target.closest('.gallery-item[data-gallery]');
|
|
if (!item) return;
|
|
this.open(item.dataset.gallery, parseInt(item.dataset.index, 10));
|
|
});
|
|
}
|
|
|
|
_initTabs() {
|
|
document.querySelectorAll('.gallery-tab').forEach((tab) => {
|
|
tab.addEventListener('click', () => {
|
|
// Mise à jour des onglets actifs
|
|
document.querySelectorAll('.gallery-tab').forEach(t => {
|
|
t.classList.remove('active');
|
|
t.setAttribute('aria-selected', 'false');
|
|
});
|
|
tab.classList.add('active');
|
|
tab.setAttribute('aria-selected', 'true');
|
|
|
|
// Affichage du panneau correspondant
|
|
const roomId = tab.dataset.room;
|
|
const panelId = roomId === 'general' ? 'room-general' : `room-${roomId}`;
|
|
document.querySelectorAll('.gallery-grid').forEach(g => g.classList.remove('active'));
|
|
const panel = document.getElementById(panelId);
|
|
if (panel) panel.classList.add('active');
|
|
});
|
|
});
|
|
}
|
|
|
|
// ── API publique ───────────────────────────────────────────────────────────
|
|
|
|
open(key, index) {
|
|
this.currentKey = key;
|
|
this.currentIndex = index;
|
|
this._render();
|
|
this.modal.hidden = false;
|
|
document.body.style.overflow = 'hidden';
|
|
this.btnClose.focus();
|
|
}
|
|
|
|
close() {
|
|
this.modal.hidden = true;
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
prev() {
|
|
const photos = this.data[this.currentKey] || [];
|
|
this.currentIndex = (this.currentIndex - 1 + photos.length) % photos.length;
|
|
this._render();
|
|
}
|
|
|
|
next() {
|
|
const photos = this.data[this.currentKey] || [];
|
|
this.currentIndex = (this.currentIndex + 1) % photos.length;
|
|
this._render();
|
|
}
|
|
|
|
// ── Rendu ──────────────────────────────────────────────────────────────────
|
|
|
|
_render() {
|
|
const photos = this.data[this.currentKey] || [];
|
|
if (!photos.length) return;
|
|
|
|
const photo = photos[this.currentIndex];
|
|
this.img.src = photo.src;
|
|
this.img.alt = photo.caption || '';
|
|
this.caption.textContent = photo.caption || '';
|
|
|
|
const total = photos.length;
|
|
const of = (window.I18N && I18N.of) ? I18N.of : '/';
|
|
this.counter.textContent = `${this.currentIndex + 1} ${of} ${total}`;
|
|
|
|
// Désactiver les flèches si une seule photo
|
|
this.btnPrev.disabled = total <= 1;
|
|
this.btnNext.disabled = total <= 1;
|
|
}
|
|
}
|