/** * Mobile-specific JavaScript for Kayal Aqua * Enhanced mobile user experience */ class MobileEnhancements { constructor() { this.init(); } init() { if (this.isMobileDevice()) { this.setupMobileFeatures(); this.setupTouchGestures(); this.setupMobileNavigation(); this.setupPullToRefresh(); this.setupMobileSearch(); this.setupBottomSheet(); this.setupQuickActions(); } } isMobileDevice() { return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 768; } setupMobileFeatures() { // Prevent zoom on input focus this.preventInputZoom(); // Handle orientation change window.addEventListener('orientationchange', () => { setTimeout(() => { this.handleOrientationChange(); }, 100); }); // Handle viewport changes this.handleViewportChanges(); // Setup swipe gestures for navigation this.setupSwipeNavigation(); // Mobile-specific form enhancements this.enhanceMobileForms(); } preventInputZoom() { // Add viewport meta tag if not present let viewport = document.querySelector('meta[name="viewport"]'); if (!viewport) { viewport = document.createElement('meta'); viewport.name = 'viewport'; document.head.appendChild(viewport); } viewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'; // Ensure input font-size is at least 16px to prevent zoom const inputs = document.querySelectorAll('input, select, textarea'); inputs.forEach(input => { if (getComputedStyle(input).fontSize < '16px') { input.style.fontSize = '16px'; } }); } handleOrientationChange() { // Refresh layout after orientation change document.body.scrollTop = 0; document.documentElement.scrollTop = 0; // Trigger resize event window.dispatchEvent(new Event('resize')); // Close any open mobile menus if (window.kayalAquaApp) { window.kayalAquaApp.closeMobileNav(); } } handleViewportChanges() { let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); window.addEventListener('resize', () => { vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); }); } setupTouchGestures() { let startX, startY, endX, endY; document.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; startY = e.touches[0].clientY; }, { passive: true }); document.addEventListener('touchend', (e) => { if (!startX || !startY) return; endX = e.changedTouches[0].clientX; endY = e.changedTouches[0].clientY; const deltaX = startX - endX; const deltaY = startY - endY; // Only handle horizontal swipes that are significant if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 100) { if (deltaX > 0) { // Swipe left this.handleSwipeLeft(); } else { // Swipe right this.handleSwipeRight(); } } // Reset values startX = startY = endX = endY = null; }, { passive: true }); } handleSwipeLeft() { // Close mobile nav if open const nav = document.querySelector('.main-nav.active'); if (nav && window.kayalAquaApp) { window.kayalAquaApp.closeMobileNav(); } } handleSwipeRight() { // Open mobile nav if closed const nav = document.querySelector('.main-nav'); if (nav && !nav.classList.contains('active') && window.kayalAquaApp) { window.kayalAquaApp.toggleMobileNav(); } } setupSwipeNavigation() { // Add swipe indicators to swipeable elements const swipeContainers = document.querySelectorAll('.swipe-container'); swipeContainers.forEach(container => { container.style.scrollSnapType = 'x mandatory'; const items = container.querySelectorAll('.swipe-item'); items.forEach(item => { item.style.scrollSnapAlign = 'start'; }); }); } setupMobileNavigation() { // Add touch-friendly navigation const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { item.addEventListener('touchstart', function() { this.style.backgroundColor = 'rgba(255, 193, 7, 0.1)'; }, { passive: true }); item.addEventListener('touchend', function() { setTimeout(() => { this.style.backgroundColor = ''; }, 150); }, { passive: true }); }); } setupPullToRefresh() { let startY = 0; let currentY = 0; let pullThreshold = 100; let isRefreshing = false; const refreshIndicator = this.createRefreshIndicator(); document.addEventListener('touchstart', (e) => { if (window.scrollY === 0) { startY = e.touches[0].clientY; } }, { passive: true }); document.addEventListener('touchmove', (e) => { if (isRefreshing || window.scrollY > 0) return; currentY = e.touches[0].clientY; const pullDistance = currentY - startY; if (pullDistance > 0) { const pullRatio = Math.min(pullDistance / pullThreshold, 1); refreshIndicator.style.transform = `translateY(${pullDistance * 0.5}px)`; refreshIndicator.style.opacity = pullRatio; if (pullDistance > pullThreshold) { refreshIndicator.textContent = 'Release to refresh'; refreshIndicator.classList.add('ready-to-refresh'); } else { refreshIndicator.textContent = 'Pull to refresh'; refreshIndicator.classList.remove('ready-to-refresh'); } } }, { passive: true }); document.addEventListener('touchend', () => { if (isRefreshing) return; const pullDistance = currentY - startY; if (pullDistance > pullThreshold) { this.triggerRefresh(refreshIndicator); } else { this.resetRefreshIndicator(refreshIndicator); } startY = 0; currentY = 0; }, { passive: true }); } createRefreshIndicator() { const indicator = document.createElement('div'); indicator.className = 'pull-refresh'; indicator.textContent = 'Pull to refresh'; indicator.style.cssText = ` position: fixed; top: -50px; left: 0; right: 0; height: 50px; background: var(--primary-navy); color: var(--white); display: flex; align-items: center; justify-content: center; font-size: 14px; opacity: 0; transform: translateY(0); transition: transform 0.3s ease, opacity 0.3s ease; z-index: 1000; `; document.body.appendChild(indicator); return indicator; } triggerRefresh(indicator) { indicator.textContent = 'Refreshing...'; indicator.style.transform = 'translateY(50px)'; indicator.style.opacity = '1'; // Simulate refresh - replace with actual refresh logic setTimeout(() => { window.location.reload(); }, 1500); } resetRefreshIndicator(indicator) { indicator.style.transform = 'translateY(0)'; indicator.style.opacity = '0'; indicator.textContent = 'Pull to refresh'; indicator.classList.remove('ready-to-refresh'); } setupMobileSearch() { const searchInputs = document.querySelectorAll('.mobile-search-input'); searchInputs.forEach(input => { const searchBtn = input.parentElement.querySelector('.mobile-search-btn'); input.addEventListener('input', this.debounce((e) => { const query = e.target.value.trim(); if (query.length > 2) { this.performMobileSearch(query); } }, 300)); if (searchBtn) { searchBtn.addEventListener('click', () => { const query = input.value.trim(); if (query) { this.performMobileSearch(query); } }); } }); } performMobileSearch(query) { // Implement search functionality based on current page const currentPage = window.location.pathname.split('/').pop(); console.log(`Searching for: ${query} on page: ${currentPage}`); // Show search results this.showSearchResults(query); } showSearchResults(query) { // Create or update search results container let resultsContainer = document.getElementById('mobile-search-results'); if (!resultsContainer) { resultsContainer = document.createElement('div'); resultsContainer.id = 'mobile-search-results'; resultsContainer.className = 'mobile-search-results'; resultsContainer.style.cssText = ` position: absolute; top: 100%; left: 0; right: 0; background: white; border: 1px solid #ddd; border-top: none; border-radius: 0 0 8px 8px; max-height: 300px; overflow-y: auto; z-index: 1000; display: none; `; const searchContainer = document.querySelector('.mobile-search'); if (searchContainer) { searchContainer.style.position = 'relative'; searchContainer.appendChild(resultsContainer); } } // Show loading state resultsContainer.innerHTML = '