This commit is contained in:
2026-05-21 15:35:54 +02:00
commit 2d39550bc8
333 changed files with 7768 additions and 0 deletions

430
script.js Normal file
View File

@@ -0,0 +1,430 @@
/* ============================================
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 });
}
});