diff --git a/themes/lucentui/js/app.js b/themes/lucentui/js/app.js index 2bf024562..68031d90a 100644 --- a/themes/lucentui/js/app.js +++ b/themes/lucentui/js/app.js @@ -1,5 +1,7 @@ // Optimized app.js const passiveOptions = { passive: true }; +// Non-passive options for handlers that need to call preventDefault() +const activeOptions = { passive: false }; document.addEventListener('livewire:init', () => { Livewire.hook('request', ({ fail }) => { @@ -148,11 +150,21 @@ function initRevealAnimations() { } const buttonClickStates = new WeakMap(); +const buttonClickHandlers = new WeakMap(); + function preventDoubleClick(selector) { const buttons = document.querySelectorAll(selector); buttons.forEach(button => { - if (buttonClickStates.has(button)) return; + // Skip if already has a handler attached + if (buttonClickHandlers.has(button)) return; + + // Skip Livewire/Alpine handled elements - let them handle their own state + if (button.hasAttribute('wire:click') || + button.hasAttribute('x-on:click') || + button.hasAttribute('@click')) { + return; + } const clickHandler = function(e) { if (buttonClickStates.get(button)) { @@ -176,8 +188,10 @@ function preventDoubleClick(selector) { } }; - button.addEventListener('click', clickHandler, passiveOptions); + // Use non-passive listener since we need to call preventDefault() + button.addEventListener('click', clickHandler, activeOptions); buttonClickStates.set(button, false); + buttonClickHandlers.set(button, clickHandler); }); } @@ -228,10 +242,20 @@ function getDevicePerformance() { return devicePerformanceLevel; } +const formSubmitHandlers = new WeakMap(); + function optimizeFormInteractions() { const forms = document.querySelectorAll('form'); forms.forEach(form => { + // Skip if already has a handler attached + if (formSubmitHandlers.has(form)) return; + + // Skip Livewire forms - they handle their own submit state + if (form.hasAttribute('wire:submit') || form.closest('[wire\\:id]')) { + return; + } + const submitBtn = form.querySelector('button[type="submit"], input[type="submit"]'); if (!submitBtn) return; @@ -246,6 +270,7 @@ function optimizeFormInteractions() { }; form.addEventListener('submit', submitHandler, passiveOptions); + formSubmitHandlers.set(form, submitHandler); }); } @@ -361,6 +386,26 @@ if (document.readyState === 'loading') { initializeApp(); } +// Reinitialize after Livewire navigation (wire:navigate) +document.addEventListener('livewire:navigated', () => { + // Re-run initialization for dynamically loaded content + updateDateTime(); + + const performance = getDevicePerformance(); + + if (performance === 'high') { + initRevealAnimations(); + initLazyLoading(); + } else { + document.querySelectorAll('.reveal-on-scroll').forEach(el => { + el.classList.add('revealed'); + }); + } + + preventDoubleClick('.btn-primary, [href*="checkout"], [href*="add-to-cart"]'); + optimizeFormInteractions(); +}); + window.addEventListener('beforeunload', () => { if (timeUpdateInterval) clearInterval(timeUpdateInterval); if (revealObserver) revealObserver.disconnect(); diff --git a/themes/lucentui/views/layouts/app.blade.php b/themes/lucentui/views/layouts/app.blade.php index ef338f62b..61d62031b 100644 --- a/themes/lucentui/views/layouts/app.blade.php +++ b/themes/lucentui/views/layouts/app.blade.php @@ -329,6 +329,15 @@ } document.addEventListener('click', function(e) { + // Skip Livewire/Alpine handled buttons - they manage their own loading states + if (e.target.hasAttribute('wire:click') || + e.target.hasAttribute('wire:submit') || + e.target.hasAttribute('x-on:click') || + e.target.hasAttribute('@click') || + e.target.closest('[wire\\:submit]')) { + return; + } + if (e.target.matches('button[type="submit"], .btn-submit')) { e.target.classList.add('btn-loading');