/* ══════════════════════════════════════════════════════ HAI.SA — Complete JavaScript Covers: Hero Slider, Cart Drawer, Toast, Category Filter, Smooth Scroll, Scroll Reveals, Active Nav Highlighting Usage: ══════════════════════════════════════════════════════ */ /* ══════════════════════════════════════ 1. HERO SLIDER ─ Auto-advances every 5 seconds ─ goSlide(n) — jump to slide index n ─ moveSlide(±1) — prev / next ─ resetAuto() — restart the timer after manual nav Required HTML IDs: #slides, .dot ══════════════════════════════════════ */ let cur = 0; const TOTAL_SLIDES = 3; let autoTimer; function goSlide(n) { cur = (n + TOTAL_SLIDES) % TOTAL_SLIDES; document.getElementById('slides').style.transform = `translateX(${cur * 100}%)`; document.querySelectorAll('.dot').forEach((dot, i) => { dot.classList.toggle('active', i === cur); }); } function moveSlide(dir) { goSlide(cur + dir); resetAuto(); } function resetAuto() { clearInterval(autoTimer); autoTimer = setInterval(() => goSlide(cur + 1), 5000); } // Kick off auto-play on page load resetAuto(); /* ══════════════════════════════════════ 2. SHOPPING CART ─ cart {} — in-memory store { productName: qty } ─ toggleCart() — open / close the drawer ─ addToCart() — add item, update UI, show toast ─ changeQty() — increment / decrement item qty ─ updateCart() — re-render drawer contents + badge Required HTML IDs: #cartBtn, #cartDrawer, #cartOverlay, #cartCount, #cartEmpty, #cartItems, #cartFooter, #cartTotal ══════════════════════════════════════ */ let cart = {}; function toggleCart() { document.getElementById('cartDrawer').classList.toggle('open'); document.getElementById('cartOverlay').classList.toggle('open'); } // Open cart when clicking the cart icon button document.getElementById('cartBtn').addEventListener('click', toggleCart); function addToCart(btn, name) { cart[name] = (cart[name] || 0) + 1; updateCart(); showToast('تمت إضافة ' + name); // Brief visual feedback on the + button btn.textContent = '✓'; btn.style.background = '#27ae60'; setTimeout(() => { btn.textContent = '+'; btn.style.background = ''; }, 1200); } function updateCart() { const totalItems = Object.values(cart).reduce((a, b) => a + b, 0); document.getElementById('cartCount').textContent = totalItems; const emptyEl = document.getElementById('cartEmpty'); const itemsEl = document.getElementById('cartItems'); const footerEl = document.getElementById('cartFooter'); if (totalItems === 0) { // Show empty state emptyEl.style.display = 'flex'; itemsEl.style.display = 'none'; footerEl.style.display = 'none'; } else { // Show cart items emptyEl.style.display = 'none'; itemsEl.style.display = 'flex'; footerEl.style.display = 'block'; // Render each cart row itemsEl.innerHTML = Object.entries(cart).map(([name, qty]) => `
☕ ${name}
${qty}
`).join(''); // Simple total — update this formula to use real prices if needed const totalPrice = Object.keys(cart).length * 89; document.getElementById('cartTotal').textContent = totalPrice + ' ر.س'; } } function changeQty(name, delta) { cart[name] = (cart[name] || 0) + delta; if (cart[name] <= 0) delete cart[name]; updateCart(); } /* ══════════════════════════════════════ 3. TOAST NOTIFICATION ─ showToast(msg) — display a bottom-center toast ─ Auto-hides after 2.8 seconds Required HTML ID: #toast ══════════════════════════════════════ */ function showToast(msg) { const toast = document.getElementById('toast'); toast.textContent = '✅ ' + msg; toast.classList.add('show'); setTimeout(() => toast.classList.remove('show'), 2800); } /* ══════════════════════════════════════ 4. CATEGORY FILTER PILLS ─ filterCat(el, cat) — set active pill ─ Extend with real filtering logic as needed Required selector: .cat-pill ══════════════════════════════════════ */ function filterCat(el, cat) { document.querySelectorAll('.cat-pill').forEach(p => p.classList.remove('active')); el.classList.add('active'); // TODO: filter product cards by `cat` value } /* ══════════════════════════════════════ 5. SMOOTH SCROLL TO SECTION ─ scrollToSection(id) — scroll to any section by ID ─ Used by hero slider CTA buttons ══════════════════════════════════════ */ function scrollToSection(id) { document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' }); } /* ══════════════════════════════════════ 6. SCROLL REVEAL ANIMATIONS ─ Any element with class .reveal will fade + slide up when it enters the viewport ─ Staggered with 60ms delay per element Required CSS: .reveal { opacity:0; transform:translateY(24px); } .reveal.visible { opacity:1; transform:none; } ══════════════════════════════════════ */ const revealEls = document.querySelectorAll('.reveal'); const revealObserver = new IntersectionObserver((entries) => { entries.forEach((entry, i) => { if (entry.isIntersecting) { setTimeout(() => entry.target.classList.add('visible'), i * 60); revealObserver.unobserve(entry.target); // Only animate once } }); }, { threshold: 0.1 }); revealEls.forEach(el => revealObserver.observe(el)); /* ══════════════════════════════════════ 7. ACTIVE NAV LINK ON SCROLL ─ Highlights the matching nav link as the user scrolls through named sections ─ Edit `sections` array to match your section IDs Required selectors: .nav-list a ══════════════════════════════════════ */ window.addEventListener('scroll', () => { const sections = ['collections', 'new', 'premium', 'winter', 'sachets']; const navLinks = document.querySelectorAll('.nav-list a'); let current = ''; sections.forEach(id => { const el = document.getElementById(id); if (el && window.scrollY >= el.offsetTop - 120) current = id; }); navLinks.forEach(link => { link.classList.remove('active'); if (link.getAttribute('href') === '#' + current) { link.classList.add('active'); } }); });