/** * Product Page Mobile UX Enhancements * تحسينات UX لصفحة المنتج - محسّنة للموبايل */ (function() { 'use strict'; // ============================================ // 1. Sticky Bottom Bar - زر ثابت في الأسفل // ============================================ function initStickyBottomBar() { const stickyBar = document.querySelector('.sticky-bottom-bar'); if (!stickyBar) return; let lastScroll = 0; const threshold = 300; // يظهر بعد التمرير 300px window.addEventListener('scroll', () => { const currentScroll = window.pageYOffset; if (currentScroll > threshold) { stickyBar.classList.add('visible'); } else { stickyBar.classList.remove('visible'); } lastScroll = currentScroll; }); } // ============================================ // 2. Swipeable Image Gallery - معرض صور بالسحب // ============================================ function initImageGallery() { const gallery = document.querySelector('.product-image-gallery'); if (!gallery) return; const container = gallery.querySelector('.gallery-container'); const slides = gallery.querySelectorAll('.gallery-slide'); const indicators = gallery.querySelectorAll('.gallery-indicator'); const prevBtn = gallery.querySelector('.gallery-nav.prev'); const nextBtn = gallery.querySelector('.gallery-nav.next'); if (!container || slides.length === 0) return; let currentIndex = 0; let startX = 0; let currentX = 0; let isDragging = false; // Update slide position function updateSlide(index, animate = true) { if (index < 0) index = 0; if (index >= slides.length) index = slides.length - 1; currentIndex = index; const offset = -index * 100; if (animate) { container.style.transition = 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; } else { container.style.transition = 'none'; } container.style.transform = `translateX(${offset}%)`; // Update indicators indicators.forEach((indicator, i) => { indicator.classList.toggle('active', i === index); }); } // Touch events container.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; isDragging = true; container.style.transition = 'none'; }); container.addEventListener('touchmove', (e) => { if (!isDragging) return; currentX = e.touches[0].clientX; const diff = currentX - startX; const offset = -currentIndex * 100 + (diff / container.offsetWidth) * 100; container.style.transform = `translateX(${offset}%)`; }); container.addEventListener('touchend', () => { if (!isDragging) return; const diff = currentX - startX; const threshold = container.offsetWidth * 0.2; // 20% swipe threshold if (Math.abs(diff) > threshold) { if (diff > 0) { // Swipe right - previous updateSlide(currentIndex - 1); } else { // Swipe left - next updateSlide(currentIndex + 1); } } else { // Return to current updateSlide(currentIndex); } isDragging = false; }); // Mouse events for desktop container.addEventListener('mousedown', (e) => { startX = e.clientX; isDragging = true; container.style.transition = 'none'; container.style.cursor = 'grabbing'; }); container.addEventListener('mousemove', (e) => { if (!isDragging) return; currentX = e.clientX; const diff = currentX - startX; const offset = -currentIndex * 100 + (diff / container.offsetWidth) * 100; container.style.transform = `translateX(${offset}%)`; }); container.addEventListener('mouseup', () => { if (!isDragging) return; const diff = currentX - startX; const threshold = container.offsetWidth * 0.2; if (Math.abs(diff) > threshold) { if (diff > 0) { updateSlide(currentIndex - 1); } else { updateSlide(currentIndex + 1); } } else { updateSlide(currentIndex); } isDragging = false; container.style.cursor = 'grab'; }); container.addEventListener('mouseleave', () => { if (isDragging) { updateSlide(currentIndex); isDragging = false; container.style.cursor = 'grab'; } }); // Navigation buttons if (prevBtn) { prevBtn.addEventListener('click', () => { updateSlide(currentIndex - 1); }); } if (nextBtn) { nextBtn.addEventListener('click', () => { updateSlide(currentIndex + 1); }); } // Indicator clicks indicators.forEach((indicator, index) => { indicator.addEventListener('click', () => { updateSlide(index); }); }); // Initialize updateSlide(0, false); } // ============================================ // 3. Quantity Counter - عداد الكمية // ============================================ function initQuantityCounter() { const quantitySelectors = document.querySelectorAll('.quantity-selector'); quantitySelectors.forEach(selector => { const minusBtn = selector.querySelector('.quantity-btn.minus'); const plusBtn = selector.querySelector('.quantity-btn.plus'); const valueDisplay = selector.querySelector('.quantity-value'); if (!minusBtn || !plusBtn || !valueDisplay) return; let quantity = parseInt(valueDisplay.textContent) || 1; const min = parseInt(selector.dataset.min) || 1; const max = parseInt(selector.dataset.max) || 99; function updateQuantity(newQuantity) { quantity = Math.max(min, Math.min(max, newQuantity)); valueDisplay.textContent = quantity; // Update buttons state minusBtn.disabled = quantity <= min; plusBtn.disabled = quantity >= max; // Pulse animation valueDisplay.classList.add('pulse-animation'); setTimeout(() => { valueDisplay.classList.remove('pulse-animation'); }, 300); // Trigger custom event selector.dispatchEvent(new CustomEvent('quantitychange', { detail: { quantity } })); } minusBtn.addEventListener('click', () => { updateQuantity(quantity - 1); }); plusBtn.addEventListener('click', () => { updateQuantity(quantity + 1); }); // Initialize updateQuantity(quantity); }); } // ============================================ // Initialize All // ============================================ function init() { initStickyBottomBar(); initImageGallery(); initQuantityCounter(); console.log('✓ Product Mobile UX initialized'); } // Run on DOM ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })(); // ============================================ // 4. Floating Wishlist Button - زر المفضلة عائم // ============================================ function initFloatingWishlist() { const floatingBtn = document.querySelector('.floating-wishlist-btn'); if (!floatingBtn) return; floatingBtn.addEventListener('click', function() { this.classList.toggle('active'); // Trigger wishlist toggle const productId = this.dataset.productId; if (productId && typeof toggleWishlist === 'function') { toggleWishlist(productId); } }); } // ============================================ // 5. Accordion Sections - أكورديون // ============================================ function initAccordion() { const accordionHeaders = document.querySelectorAll('.accordion-header'); accordionHeaders.forEach(header => { header.addEventListener('click', function() { const section = this.closest('.accordion-section'); const isActive = section.classList.contains('active'); // Close all sections document.querySelectorAll('.accordion-section').forEach(s => { s.classList.remove('active'); }); // Open clicked section if it wasn't active if (!isActive) { section.classList.add('active'); } }); }); // Open first section by default const firstSection = document.querySelector('.accordion-section'); if (firstSection) { firstSection.classList.add('active'); } } // ============================================ // 6. Stock Indicator - مؤشر التوفر (Static) // ============================================ // No JS needed - handled by PHP // ============================================ // 7. Related Products Carousel - منتجات مشابهة // ============================================ function initRelatedProductsCarousel() { const carousel = document.querySelector('.related-products-carousel'); if (!carousel) return; let isDown = false; let startX; let scrollLeft; carousel.addEventListener('mousedown', (e) => { isDown = true; carousel.style.cursor = 'grabbing'; startX = e.pageX - carousel.offsetLeft; scrollLeft = carousel.scrollLeft; }); carousel.addEventListener('mouseleave', () => { isDown = false; carousel.style.cursor = 'grab'; }); carousel.addEventListener('mouseup', () => { isDown = false; carousel.style.cursor = 'grab'; }); carousel.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - carousel.offsetLeft; const walk = (x - startX) * 2; carousel.scrollLeft = scrollLeft - walk; }); } // ============================================ // 8. Share Button - زر المشاركة // ============================================ function initShareButton() { const shareBtn = document.querySelector('.share-btn'); if (!shareBtn) return; shareBtn.addEventListener('click', async function() { const productName = this.dataset.productName || document.title; const productUrl = window.location.href; // Check if Web Share API is supported if (navigator.share) { try { await navigator.share({ title: productName, text: 'شاهد هذا المنتج الرائع!', url: productUrl }); } catch (err) { console.log('Share cancelled'); } } else { // Fallback: Copy to clipboard try { await navigator.clipboard.writeText(productUrl); showToast('تم نسخ الرابط!'); } catch (err) { console.error('Failed to copy:', err); } } }); } // ============================================ // 9. Sticky Price Bar - سعر متحرك // ============================================ function initStickyPriceBar() { const stickyPriceBar = document.querySelector('.sticky-price-bar'); if (!stickyPriceBar) return; const productSection = document.querySelector('.product-main-info'); if (!productSection) return; const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { stickyPriceBar.classList.remove('visible'); } else { stickyPriceBar.classList.add('visible'); } }, { threshold: 0 } ); observer.observe(productSection); } // ============================================ // 10. Interactive Rating - تقييم تفاعلي // ============================================ function initInteractiveRating() { const ratingStars = document.querySelectorAll('.rating-star'); if (ratingStars.length === 0) return; let selectedRating = 0; ratingStars.forEach((star, index) => { // Hover effect star.addEventListener('mouseenter', () => { ratingStars.forEach((s, i) => { if (i <= index) { s.classList.add('hover'); } else { s.classList.remove('hover'); } }); }); // Click to select star.addEventListener('click', () => { selectedRating = index + 1; updateRatingDisplay(selectedRating); // Trigger custom event const event = new CustomEvent('ratingselected', { detail: { rating: selectedRating } }); document.dispatchEvent(event); // Show toast if (typeof showToast === 'function') { showToast(`تم اختيار ${selectedRating} نجوم ⭐`); } }); }); // Remove hover on mouse leave const ratingContainer = document.querySelector('.rating-stars'); if (ratingContainer) { ratingContainer.addEventListener('mouseleave', () => { ratingStars.forEach(s => s.classList.remove('hover')); }); } function updateRatingDisplay(rating) { ratingStars.forEach((star, index) => { if (index < rating) { star.classList.add('filled'); } else { star.classList.remove('filled'); } }); } } // ============================================ // Helper: Show Toast (if not already defined) // ============================================ if (typeof showToast !== 'function') { window.showToast = function(message, duration = 3000) { let toast = document.getElementById('mobile-ux-toast'); if (!toast) { toast = document.createElement('div'); toast.id = 'mobile-ux-toast'; toast.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #E57393 0%, #D1537A 100%); color: white; padding: 16px 24px; border-radius: 12px; box-shadow: 0 8px 24px rgba(229, 115, 147, 0.4); z-index: 9999; font-weight: 600; font-size: 16px; opacity: 0; transition: opacity 0.3s; `; document.body.appendChild(toast); } toast.textContent = message; toast.style.opacity = '1'; setTimeout(() => { toast.style.opacity = '0'; }, duration); }; } // ============================================ // Update Initialize Function // ============================================ // Replace the old init() with this updated version const oldInit = init; init = function() { oldInit(); initFloatingWishlist(); initAccordion(); initRelatedProductsCarousel(); initShareButton(); initStickyPriceBar(); initInteractiveRating(); console.log('✓ All Mobile UX features initialized'); };