/* ══════════════════════════════════════════════════════
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]) => `
`).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');
}
});
});