/** * 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; } }