/* ============================================ RESTAURANT COLIBRI — JavaScript ============================================ */ document.addEventListener('DOMContentLoaded', () => { // ===== NAVIGATION — Scroll Effect ===== const nav = document.getElementById('nav'); const handleNavScroll = () => { if (window.scrollY > 50) { nav.classList.add('nav--scrolled'); } else { nav.classList.remove('nav--scrolled'); } }; window.addEventListener('scroll', handleNavScroll, { passive: true }); handleNavScroll(); // ===== HERO CAROUSEL ===== const heroSlides = document.querySelectorAll('.hero__slide'); const heroDots = document.querySelectorAll('.hero__dot'); const heroPrev = document.getElementById('heroPrev'); const heroNext = document.getElementById('heroNext'); let currentSlide = 0; let heroInterval = null; const goToSlide = (index) => { heroSlides[currentSlide].classList.remove('active'); heroDots[currentSlide].classList.remove('active'); currentSlide = (index + heroSlides.length) % heroSlides.length; heroSlides[currentSlide].classList.add('active'); heroDots[currentSlide].classList.add('active'); }; const startHeroAutoplay = () => { heroInterval = setInterval(() => goToSlide(currentSlide + 1), 6000); }; const resetHeroAutoplay = () => { clearInterval(heroInterval); startHeroAutoplay(); }; if (heroSlides.length > 1) { heroPrev.addEventListener('click', () => { goToSlide(currentSlide - 1); resetHeroAutoplay(); }); heroNext.addEventListener('click', () => { goToSlide(currentSlide + 1); resetHeroAutoplay(); }); heroDots.forEach(dot => { dot.addEventListener('click', () => { goToSlide(parseInt(dot.dataset.slide)); resetHeroAutoplay(); }); }); startHeroAutoplay(); // Touch swipe support const heroEl = document.querySelector('.hero'); let touchStartX = 0; let touchEndX = 0; heroEl.addEventListener('touchstart', (e) => { touchStartX = e.changedTouches[0].screenX; }, { passive: true }); heroEl.addEventListener('touchend', (e) => { touchEndX = e.changedTouches[0].screenX; const diff = touchStartX - touchEndX; if (Math.abs(diff) > 50) { if (diff > 0) { goToSlide(currentSlide + 1); } else { goToSlide(currentSlide - 1); } resetHeroAutoplay(); } }, { passive: true }); } // ===== MOBILE MENU ===== const burger = document.getElementById('navBurger'); const mobileMenu = document.getElementById('mobileMenu'); const mobileLinks = document.querySelectorAll('.mobile-menu__link, .mobile-menu__cta'); if (burger && mobileMenu) { burger.addEventListener('click', () => { burger.classList.toggle('active'); mobileMenu.classList.toggle('active'); document.body.style.overflow = mobileMenu.classList.contains('active') ? 'hidden' : ''; }); mobileLinks.forEach(link => { link.addEventListener('click', () => { burger.classList.remove('active'); mobileMenu.classList.remove('active'); document.body.style.overflow = ''; }); }); } // ===== MENU TABS (with ARIA) ===== const tabs = document.querySelectorAll('.menu__tab'); const contents = document.querySelectorAll('.menu__content'); // Set ARIA roles const tabList = document.querySelector('.menu__tabs'); if (tabList) tabList.setAttribute('role', 'tablist'); tabs.forEach((tab, i) => { const target = tab.dataset.tab; tab.setAttribute('role', 'tab'); tab.setAttribute('aria-controls', `tab-${target}`); tab.setAttribute('aria-selected', tab.classList.contains('active') ? 'true' : 'false'); tab.id = `tab-btn-${target}`; }); contents.forEach(content => { content.setAttribute('role', 'tabpanel'); const id = content.id.replace('tab-', ''); content.setAttribute('aria-labelledby', `tab-btn-${id}`); }); tabs.forEach(tab => { tab.addEventListener('click', () => { const target = tab.dataset.tab; // Update active tab + ARIA tabs.forEach(t => { t.classList.remove('active'); t.setAttribute('aria-selected', 'false'); }); tab.classList.add('active'); tab.setAttribute('aria-selected', 'true'); // Show target content contents.forEach(content => { content.classList.remove('active'); content.classList.remove('scrolled-end'); if (content.id === `tab-${target}`) { content.classList.add('active'); // Reset scroll position & check scroll indicator const grid = content.querySelector('.menu__grid'); if (grid) { grid.scrollLeft = 0; checkScrollEnd(content, grid); } } }); }); }); // ===== SCROLL INDICATOR — fade gradient at end ===== const checkScrollEnd = (contentEl, gridEl) => { const atEnd = gridEl.scrollLeft + gridEl.clientWidth >= gridEl.scrollWidth - 10; if (atEnd) { contentEl.classList.add('scrolled-end'); } else { contentEl.classList.remove('scrolled-end'); } }; document.querySelectorAll('.menu__grid').forEach(grid => { grid.addEventListener('scroll', () => { const contentEl = grid.closest('.menu__content'); if (contentEl) checkScrollEnd(contentEl, grid); }, { passive: true }); }); // Initial check for active tab const activeContent = document.querySelector('.menu__content.active'); if (activeContent) { const activeGrid = activeContent.querySelector('.menu__grid'); if (activeGrid) checkScrollEnd(activeContent, activeGrid); } // ===== DIETARY FILTERS (multi-select) ===== const filterBtns = document.querySelectorAll('.menu__filter'); const filterReset = document.getElementById('filterReset'); const allCards = document.querySelectorAll('.menu-card'); const applyFilters = () => { // Gather all active filters const activeFilters = []; filterBtns.forEach(b => { if (b.classList.contains('active')) activeFilters.push(b.dataset.filter); }); // Show/hide reset button if (filterReset) { filterReset.classList.toggle('visible', activeFilters.length > 0); } // No filters active = show everything if (activeFilters.length === 0) { allCards.forEach(card => card.classList.remove('filtered-out')); } else { allCards.forEach(card => { const diet = (card.dataset.diet || '').split(' ').filter(Boolean); const hasNuts = card.dataset.noix === 'true'; // Card must match ALL active filters (AND logic) const pass = activeFilters.every(f => { if (f === 'sg') return diet.includes('sg'); if (f === 'v') return diet.includes('v') || diet.includes('vg'); if (f === 'vg') return diet.includes('vg'); if (f === 'noix') return !hasNuts; return true; }); card.classList.toggle('filtered-out', !pass); }); } // Empty-state per tab panel document.querySelectorAll('.menu__content').forEach(panel => { const grids = panel.querySelectorAll('.menu__grid'); let allHidden = true; grids.forEach(grid => { if (grid.querySelectorAll('.menu-card:not(.filtered-out)').length > 0) allHidden = false; }); let emptyMsg = panel.querySelector('.menu__empty-msg'); if (allHidden && activeFilters.length > 0) { if (!emptyMsg) { emptyMsg = document.createElement('p'); emptyMsg.className = 'menu__empty-msg visible'; emptyMsg.textContent = 'Aucun item ne correspond à ces filtres dans cette catégorie.'; panel.appendChild(emptyMsg); } else { emptyMsg.classList.add('visible'); } } else if (emptyMsg) { emptyMsg.classList.remove('visible'); } }); }; // Toggle individual filter chips filterBtns.forEach(btn => { btn.addEventListener('click', () => { btn.classList.toggle('active'); applyFilters(); }); }); // Reset all filters if (filterReset) { filterReset.addEventListener('click', () => { filterBtns.forEach(b => b.classList.remove('active')); applyFilters(); }); } // ===== SMOOTH SCROLL for anchor links ===== document.querySelectorAll('a[href^="#"]').forEach(link => { link.addEventListener('click', (e) => { const targetId = link.getAttribute('href'); if (targetId === '#') return; const target = document.querySelector(targetId); if (target) { e.preventDefault(); const navHeight = window.innerWidth < 768 ? 64 : 80; const top = target.getBoundingClientRect().top + window.scrollY - navHeight; window.scrollTo({ top, behavior: 'smooth' }); } }); }); // ===== SCROLL REVEAL ANIMATION ===== const revealElements = document.querySelectorAll( '.section__header, .menu-card, .value-card, ' + '.traiteur__visual, .traiteur__content, ' + '.contact__info, .contact__form, .traiteur__feature' ); revealElements.forEach(el => { el.classList.add('reveal'); }); const revealOnScroll = () => { const windowHeight = window.innerHeight; document.querySelectorAll('.reveal').forEach(el => { const elementTop = el.getBoundingClientRect().top; const revealPoint = windowHeight - 80; if (elementTop < revealPoint) { el.classList.add('visible'); } }); }; window.addEventListener('scroll', revealOnScroll, { passive: true }); revealOnScroll(); // trigger on load // ===== ACTIVE NAV LINK on scroll ===== const sections = document.querySelectorAll('section[id]'); const navLinks = document.querySelectorAll('.nav__link'); const highlightNav = () => { const scrollY = window.scrollY + 100; sections.forEach(section => { const top = section.offsetTop - 100; const height = section.offsetHeight; const id = section.getAttribute('id'); if (scrollY >= top && scrollY < top + height) { navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === `#${id}`) { link.classList.add('active'); } }); } }); }; window.addEventListener('scroll', highlightNav, { passive: true }); // ===== CONTACT FORM — FormSubmit.co (AJAX) ===== const contactForm = document.getElementById('contactForm'); if (contactForm) { const btn = contactForm.querySelector('button[type="submit"]'); const btnOriginalText = btn.textContent; contactForm.addEventListener('submit', (e) => { e.preventDefault(); btn.textContent = 'Envoi en cours...'; btn.disabled = true; const formData = new FormData(contactForm); fetch(contactForm.action, { method: 'POST', body: formData, headers: { 'Accept': 'application/json' } }) .then(response => { if (response.ok) { // Success contactForm.reset(); btn.textContent = 'Message envoyé ✓'; btn.style.background = 'var(--color-vert)'; setTimeout(() => { btn.textContent = btnOriginalText; btn.disabled = false; btn.style.background = ''; }, 4000); } else { throw new Error('Erreur serveur'); } }) .catch(() => { btn.textContent = 'Erreur — Réessayer'; btn.style.background = '#c0392b'; btn.disabled = false; setTimeout(() => { btn.textContent = btnOriginalText; btn.style.background = ''; }, 4000); }); }); } // ===== MOBILE STICKY CTA ===== const mobileCta = document.getElementById('mobileCta'); if (mobileCta) { const heroSection = document.getElementById('hero'); const footerEl = document.querySelector('.footer'); const toggleMobileCta = () => { if (window.innerWidth > 768) { mobileCta.classList.remove('visible'); return; } const heroBottom = heroSection ? heroSection.offsetTop + heroSection.offsetHeight : 600; const footerTop = footerEl ? footerEl.offsetTop : Infinity; const scrollBottom = window.scrollY + window.innerHeight; // Hide when a section CTA button is visible on screen const sectionBtns = document.querySelectorAll('.menu__footer .btn, .contact__form .btn'); let sectionCtaVisible = false; sectionBtns.forEach(btn => { const rect = btn.getBoundingClientRect(); if (rect.top < window.innerHeight && rect.bottom > 0) { sectionCtaVisible = true; } }); if (window.scrollY > heroBottom && scrollBottom < footerTop + 40 && !sectionCtaVisible) { mobileCta.classList.add('visible'); } else { mobileCta.classList.remove('visible'); } }; window.addEventListener('scroll', toggleMobileCta, { passive: true }); toggleMobileCta(); } // ===== PARALLAX subtle on hero ===== const heroContent = document.querySelector('.hero__content'); if (heroContent && window.innerWidth > 768) { window.addEventListener('scroll', () => { const scrollY = window.scrollY; if (scrollY < window.innerHeight) { heroContent.style.transform = `translateY(${scrollY * 0.15}px)`; heroContent.style.opacity = 1 - (scrollY / (window.innerHeight * 0.8)); } }, { passive: true }); } });