1// services/Carousel.js 2import { i18n } from "../services/I18nService.js"; 3import { mediaService } from "../services/MediaService.js"; 4 5export function initCarousel(gallery) { 6 const track = document.querySelector('.carousel-track'); 7 const container = document.querySelector('.carousel-container'); 8 const nextBtn = document.querySelector('.carousel-next'); 9 const prevBtn = document.querySelector('.carousel-prev'); 10 const popupModal = document.getElementById('image-popup-modal'); 11 const popupImg = document.getElementById('popup-image-content'); 12 const popupCloseBtn = document.querySelector('[data-modal-close]'); 13 14 let currentIndex = 0; 15 let autoPlayInterval; 16 const slidesCount = (gallery?.length || 0); 17 18 const goToSlide = (index) => { 19 if (!track || !container) return; 20 const width = container.offsetWidth; 21 track.style.transform = `translateX(-${width * index}px)`; 22 currentIndex = index; 23 }; 24 25 const startAutoPlay = () => { 26 stopAutoPlay(); 27 autoPlayInterval = setInterval(() => { 28 let nextIndex = (currentIndex + 1) % slidesCount; 29 goToSlide(nextIndex); 30 }, 10000); 31 }; 32 33 const stopAutoPlay = () => { 34 if (autoPlayInterval) clearInterval(autoPlayInterval); 35 }; 36 37 startAutoPlay(); 38 39 if (nextBtn) { 40 nextBtn.addEventListener('click', (e) => { 41 e.stopPropagation(); 42 let nextIndex = (currentIndex + 1) % slidesCount; 43 goToSlide(nextIndex); 44 startAutoPlay(); 45 }); 46 } 47 48 if (prevBtn) { 49 prevBtn.addEventListener('click', (e) => { 50 e.stopPropagation(); 51 let prevIndex = (currentIndex - 1 + slidesCount) % slidesCount; 52 goToSlide(prevIndex); 53 startAutoPlay(); 54 }); 55 } 56 57 // Evento para abrir Popup 58 const setupPopupTrigger = (imgElement) => { 59 imgElement.style.cursor = 'zoom-in'; 60 imgElement.addEventListener('click', () => { 61 // Usamos la URL original (sin formato) para el popup 62 const originalUrl = imgElement.dataset.originalUrl || imgElement.src; 63 popupImg.src = originalUrl; 64 popupModal.classList.remove('hidden'); 65 stopAutoPlay(); 66 }); 67 }; 68 69 if (popupCloseBtn) { 70 popupCloseBtn.addEventListener('click', () => { 71 popupModal.classList.add('hidden'); 72 startAutoPlay(); 73 }); 74 } 75 76 if(popupModal){ 77 popupModal.addEventListener('click', (e) => { 78 if(e.target === popupModal) { 79 popupModal.classList.add('hidden'); 80 startAutoPlay(); 81 } 82 }); 83 } 84 85 document.querySelectorAll('.carousel-slide img').forEach(setupPopupTrigger); 86} 87 88export function renderCarousel(gallery) { 89 gallery = gallery.url ? gallery.url : gallery; 90 91 return `<div class="carousel-container relative w-full !p-0 !m-0 aspect-video rounded-[2.5rem] overflow-hidden shadow-2xl cinematic-shadow border-4 border-white group bg-slate-200"> 92 <div class="carousel-track flex h-full w-full transition-transform duration-500 ease-out"> 93 ${gallery?.map(img => ` 94 <div class="carousel-slide min-w-full h-full flex-shrink-0 flex flex-col"> 95 <img src="${mediaService.getImageUrl(img, 'large')}" class="w-full h-full object-cover object-center rounded-[2.5rem]" data-orientation-fit="auto" alt="Vista de GalerÃa"> 96 <div class="absolute ${img.caption ? '' : 'hidden'} w-max max-w-[400px] rounded-[2.5rem] bottom-0 bg-neutral-600/70 text-white m-3 p-4 flex items-center gap-3 justify-start"> 97 <p class="font-bold text-lg"> 98 ${img.caption || ''} 99 </p> 100 </div> 101 </div> 102 `).join('') || ''} 103 </div> 104 105 <button class="carousel-prev ${gallery.length > 1 ? '' : 'hidden'} absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/30 backdrop-blur-md rounded-full flex items-center justify-center text-white border border-white/50 opacity-0 group-hover:opacity-100 transition-all hover:bg-white/50 hover:scale-110 shadow-lg z-10"> 106 <i data-lucide="chevron-left" class="w-6 h-6"></i> 107 </button> 108 <button class="carousel-next ${gallery.length > 1 ? '' : 'hidden'} absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/30 backdrop-blur-md rounded-full flex items-center justify-center text-white border border-white/50 opacity-0 group-hover:opacity-100 transition-all hover:bg-white/50 hover:scale-110 shadow-lg z-10"> 109 <i data-lucide="chevron-right" class="w-6 h-6"></i> 110 </button> 111 </div> 112 ` 113} 114 115export function renderPopup() { 116 const t = (key) => i18n.t(key); 117 118 return `<div id="image-popup-modal" class="modal-overlay hidden fixed inset-0 z-[100] flex items-center justify-center bg-slate-900/95 backdrop-blur-xl p-4"> 119 <div class="relative max-w-6xl w-full flex flex-col items-end"> 120 <button data-modal-close class="text-white hover:text-cdl-gold transition-colors flex items-center gap-2 font-black uppercase text-xs tracking-widest mb-6">
121 <span>${t('plato_detail.image_close') || 'Cerrar'}</span> <i data-lucide="x" class="w-6 h-6"></i> 122 </button> 123 <img id="popup-image-content" src="" class="w-full h-auto max-h-[80vh] object-contain rounded-2xl shadow-2xl border border-white/10"> 124 </div> 125 </div>` 126}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.