refactor: Migrate header dynamic styling and mobile menu logic from vanilla JS to Alpine.js.
This commit is contained in:
parent
9cb943e360
commit
beeece5ee8
@ -52,3 +52,8 @@ @layer components {
|
||||
border: 1px red solid;
|
||||
} */
|
||||
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -24,154 +24,7 @@
|
||||
|
||||
@fluxScripts
|
||||
|
||||
<script>
|
||||
document.addEventListener('livewire:navigated', () => {
|
||||
const navbar = document.getElementById('navbar');
|
||||
const navLinks = document.getElementById('nav-links');
|
||||
const cart = document.getElementById('cart');
|
||||
const login = document.getElementById('login');
|
||||
const profile = document.getElementById('profile');
|
||||
const cartIcon = document.getElementById('cart-icon');
|
||||
const cartSvg = document.getElementById('cart-svg');
|
||||
const profileIcon = document.getElementById('profile-icon');
|
||||
const profileSvg = document.getElementById('profile-svg');
|
||||
|
||||
const mobileBtn = document.getElementById('mobile-menu-btn');
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
const hamburgerIcon = document.getElementById('hamburger-icon');
|
||||
const closeIcon = document.getElementById('close-icon');
|
||||
|
||||
function setIconsLight() {
|
||||
cartIcon.classList.remove('bg-home-foreground', 'bg-gray-100');
|
||||
cartIcon.classList.add('bg-white');
|
||||
cartSvg.classList.remove('text-white', 'text-home-foreground');
|
||||
cartSvg.classList.add('text-home-foreground');
|
||||
|
||||
profileIcon.classList.remove('bg-home-foreground', 'bg-gray-100');
|
||||
profileIcon.classList.add('bg-white');
|
||||
profileSvg.classList.remove('text-white', 'text-home-foreground');
|
||||
profileSvg.classList.add('text-home-foreground');
|
||||
}
|
||||
|
||||
function setIconsDark() {
|
||||
cartIcon.classList.add('bg-home-foreground');
|
||||
cartIcon.classList.remove('bg-white', 'bg-gray-100');
|
||||
cartSvg.classList.add('text-white');
|
||||
cartSvg.classList.remove('text-home-foreground', 'text-home-foreground');
|
||||
|
||||
profileIcon.classList.add('bg-home-foreground');
|
||||
profileIcon.classList.remove('bg-white', 'bg-gray-100');
|
||||
profileSvg.classList.add('text-white');
|
||||
profileSvg.classList.remove('text-home-foreground', 'text-home-foreground');
|
||||
}
|
||||
|
||||
function setIconsScrolled() {
|
||||
cartIcon.classList.add('bg-gray-100');
|
||||
cartIcon.classList.remove('bg-home-foreground', 'bg-white');
|
||||
cartSvg.classList.add('text-home-foreground');
|
||||
cartSvg.classList.remove('text-white');
|
||||
|
||||
profileIcon.classList.add('bg-gray-100');
|
||||
profileIcon.classList.remove('bg-home-foreground', 'bg-white');
|
||||
profileSvg.classList.add('text-home-foreground');
|
||||
profileSvg.classList.remove('text-white');
|
||||
}
|
||||
|
||||
mobileBtn.addEventListener('click', () => {
|
||||
const isClosed = mobileMenu.classList.contains('hidden');
|
||||
|
||||
if (isClosed) {
|
||||
mobileMenu.classList.remove('hidden');
|
||||
void mobileMenu.offsetWidth;
|
||||
mobileMenu.classList.remove('opacity-0');
|
||||
mobileMenu.classList.add('opacity-100');
|
||||
|
||||
hamburgerIcon.classList.add('hidden');
|
||||
closeIcon.classList.remove('hidden');
|
||||
mobileBtn.setAttribute('aria-expanded', 'true');
|
||||
|
||||
navbar.classList.remove('bg-white/40', 'backdrop-blur-md', 'shadow-sm',
|
||||
'border-gray-200');
|
||||
navbar.classList.add('bg-transparent');
|
||||
|
||||
setIconsLight();
|
||||
|
||||
} else {
|
||||
mobileMenu.classList.remove('opacity-100');
|
||||
mobileMenu.classList.add('opacity-0');
|
||||
|
||||
setTimeout(() => {
|
||||
mobileMenu.classList.add('hidden');
|
||||
}, 500);
|
||||
|
||||
hamburgerIcon.classList.remove('hidden');
|
||||
closeIcon.classList.add('hidden');
|
||||
mobileBtn.setAttribute('aria-expanded', 'false');
|
||||
|
||||
const scrolled = window.scrollY > 10;
|
||||
if (scrolled) {
|
||||
navbar.classList.add('bg-white/40', 'backdrop-blur-md', 'shadow-sm',
|
||||
'border-gray-200');
|
||||
navbar.classList.remove('bg-transparent');
|
||||
setIconsScrolled();
|
||||
} else {
|
||||
navbar.classList.add('bg-transparent');
|
||||
navbar.classList.remove('bg-white/40', 'backdrop-blur-md', 'shadow-sm',
|
||||
'border-gray-200');
|
||||
setIconsDark();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const isMenuOpen = !mobileMenu.classList.contains('hidden');
|
||||
if (isMenuOpen) return;
|
||||
|
||||
const scrolled = window.scrollY > 10;
|
||||
|
||||
if (scrolled) {
|
||||
navbar.classList.add('bg-white/40', 'backdrop-blur-md', 'shadow-sm', 'border-gray-200');
|
||||
navbar.classList.remove('bg-transparent');
|
||||
|
||||
navLinks.classList.add('bg-white', 'border-home-accent');
|
||||
navLinks.classList.remove('bg-transparent', 'border-home-foreground');
|
||||
|
||||
cart.classList.add('border-home-accent', 'bg-white');
|
||||
cart.classList.remove('border-home-foreground');
|
||||
cart.querySelector('span').classList.add('text-home-foreground');
|
||||
|
||||
login.classList.add('border-home-accent', 'text-home-foreground', 'bg-white');
|
||||
login.classList.remove('border-home-foreground');
|
||||
|
||||
profile.classList.add('border-home-accent');
|
||||
profile.classList.remove('border-home-foreground');
|
||||
|
||||
setIconsScrolled();
|
||||
} else {
|
||||
navbar.classList.add('bg-transparent');
|
||||
navbar.classList.remove('bg-white/40', 'backdrop-blur-md', 'shadow-sm', 'border-b',
|
||||
'border-gray-200');
|
||||
|
||||
navLinks.classList.add('bg-transparent', 'border-home-foreground',
|
||||
'text-home-foreground');
|
||||
navLinks.classList.remove('bg-white', 'border-home-accent');
|
||||
|
||||
cart.classList.add('border-home-foreground');
|
||||
cart.classList.remove('border-home-accent', 'bg-white');
|
||||
cart.querySelector('span').classList.remove('text-home-foreground');
|
||||
cart.querySelector('span').classList.add('text-home-foreground');
|
||||
|
||||
login.classList.add('border-home-foreground', 'text-home-foreground');
|
||||
login.classList.remove('border-home-accent', 'bg-white');
|
||||
|
||||
profile.classList.add('border-home-foreground');
|
||||
profile.classList.remove('border-home-accent');
|
||||
|
||||
setIconsDark();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@ -1,19 +1,33 @@
|
||||
<header id="navbar"
|
||||
class="fixed top-0 left-0 w-full p-4 lg:px-10 flex justify-between items-center z-50 transition-all duration-200 bg-transparent">
|
||||
<header id="navbar" x-data="{
|
||||
isOpen: false,
|
||||
scrolled: false,
|
||||
init() {
|
||||
this.scrolled = window.scrollY > 10;
|
||||
window.addEventListener('scroll', () => {
|
||||
this.scrolled = window.scrollY > 10;
|
||||
});
|
||||
}
|
||||
}"
|
||||
:class="{
|
||||
'bg-white/40 backdrop-blur-md shadow-sm border-gray-200': scrolled && !isOpen,
|
||||
'bg-transparent': !scrolled || isOpen
|
||||
}"
|
||||
class="fixed top-0 left-0 w-full p-4 lg:px-10 flex justify-between items-center z-50 transition-all duration-200">
|
||||
|
||||
<div class="flex lg:hidden items-center z-50">
|
||||
<button id="mobile-menu-btn" aria-label="Toggle Mobile Menu" aria-expanded="false" aria-controls="mobile-menu"
|
||||
<button id="mobile-menu-btn" aria-label="Toggle Mobile Menu" :aria-expanded="isOpen" aria-controls="mobile-menu"
|
||||
@click="isOpen = !isOpen"
|
||||
class="rounded-full w-10 h-10 flex items-center justify-center hover:bg-white/10 transition-colors relative group">
|
||||
|
||||
<svg id="hamburger-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor"
|
||||
stroke-width="1.5" stroke="currentColor" x-show="!isOpen"
|
||||
class="size-6 text-home-foreground transition-colors duration-300">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
|
||||
</svg>
|
||||
|
||||
<svg id="close-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor"
|
||||
class="size-6 text-white hidden transition-transform duration-300">
|
||||
stroke-width="1.5" stroke="currentColor" x-show="isOpen" x-cloak
|
||||
class="size-6 text-white transition-transform duration-300">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
@ -25,6 +39,10 @@ class="w-10 h-8 lg:w-16 lg:h-12 transition-all duration-200">
|
||||
</a>
|
||||
|
||||
<nav id="nav-links"
|
||||
:class="{
|
||||
'bg-white border-home-accent': scrolled && !isOpen,
|
||||
'bg-transparent border-home-foreground': !scrolled || isOpen
|
||||
}"
|
||||
class="hidden lg:flex items-center gap-6 font-medium px-8 py-3 rounded-full transition-all duration-200 border text-home-foreground"
|
||||
aria-label="Main Navigation">
|
||||
@foreach ($header as $item)
|
||||
@ -37,30 +55,25 @@ class="{{ request()->routeIs($item['route']) ? 'font-bold text-home-primary' : '
|
||||
</nav>
|
||||
|
||||
<section class="flex items-center lg:gap-2 gap-1 z-50">
|
||||
{{-- not used yet --}}
|
||||
{{-- <button id="cart"
|
||||
class="flex items-center rounded-full transition-all duration-200 border border-home-foreground lg:p-0 hover:scale-110">
|
||||
<div id="cart-icon"
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full transition-all duration-200 text-white bg-home-foreground">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="size-4 lg:size-6 transition-all duration-200" id="cart-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M15.75 10.5V6a3.75 3.75 0 1 0-7.5 0v4.5m11.356-1.993 1.263 12c.07.665-.45 1.243-1.119 1.243H4.25a1.125 1.125 0 0 1-1.12-1.243l1.264-12A1.125 1.125 0 0 1 5.513 7.5h12.974c.576 0 1.059.435 1.119 1.007ZM8.625 10.5a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm7.5 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<span class="ms-2 me-3 transition-all duration-200 hidden lg:block text-home-foreground">Keranjang</span>
|
||||
</button> --}}
|
||||
|
||||
@guest
|
||||
<a href="{{ route('login') }}" id="login" wire:navigate
|
||||
class="hidden lg:flex items-center border px-4 py-2 rounded-full transition-all duration-200 hover:scale-105 font-medium border-home-foreground text-home-foreground">
|
||||
:class="{
|
||||
'border-home-accent text-home-foreground bg-white': scrolled && !isOpen,
|
||||
'border-home-foreground text-home-foreground': !scrolled || isOpen
|
||||
}"
|
||||
class="hidden lg:flex items-center border px-4 py-2 rounded-full transition-all duration-200 hover:scale-105 font-medium">
|
||||
Masuk
|
||||
</a>
|
||||
|
||||
<button id="profile-mobile" wire:navigate
|
||||
class="flex lg:hidden items-center rounded-full transition-all duration-200 lg:p-0 lg:border-0 hover:scale-110">
|
||||
<div id="profile-icon-mobile"
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full transition-all duration-200 text-white bg-home-foreground">
|
||||
:class="{
|
||||
'bg-white text-home-foreground': isOpen,
|
||||
'bg-gray-100 text-home-foreground': scrolled && !isOpen,
|
||||
'bg-home-foreground text-white': !scrolled && !isOpen
|
||||
}"
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full transition-all duration-200">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="size-4 lg:size-6 transition-all duration-200">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
@ -74,9 +87,14 @@ class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full tra
|
||||
<a href="{{ route('profile.redirect') }}" id="profile"
|
||||
class="flex items-center rounded-full transition-all duration-200 lg:p-0 lg:border-0 hover:scale-110">
|
||||
<div id="profile-icon"
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full transition-all duration-200 text-white bg-home-foreground">
|
||||
:class="{
|
||||
'bg-white text-home-foreground': isOpen,
|
||||
'bg-gray-100 text-home-foreground': scrolled && !isOpen,
|
||||
'bg-home-foreground text-white': !scrolled && !isOpen
|
||||
}"
|
||||
class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full transition-all duration-200">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="size-4 lg:size-6 transition-all duration-200" id="profile-svg">
|
||||
stroke="currentColor" class="size-4 lg:size-6 transition-all duration-200">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
|
||||
</svg>
|
||||
@ -85,21 +103,49 @@ class="w-8 h-8 lg:w-10 lg:h-10 flex justify-center items-center rounded-full tra
|
||||
@endauth
|
||||
</section>
|
||||
|
||||
<nav id="mobile-menu"
|
||||
class="fixed inset-0 h-screen w-screen bg-home-foreground/80 backdrop-blur-md z-40 flex flex-col items-center justify-center gap-8 hidden transition-all duration-200 opacity-0"
|
||||
<nav id="mobile-menu" x-show="isOpen" x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 transform -translate-y-4"
|
||||
x-transition:enter-end="opacity-100 transform translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 transform translate-y-0"
|
||||
x-transition:leave-end="opacity-0 transform -translate-y-4" x-cloak
|
||||
class="fixed inset-0 h-screen w-screen bg-home-foreground/80 backdrop-blur-md z-40 flex flex-col items-center justify-center gap-8 shadow-2xl"
|
||||
aria-label="Mobile Navigation">
|
||||
|
||||
<a href="{{ route('homepage') }}" wire:navigate
|
||||
class="text-3xl font-bold text-white hover:text-gray-300 transition-colors">Beranda</a>
|
||||
<a href="{{ route('outlet') }}" wire:navigate
|
||||
class="text-3xl font-bold text-white hover:text-gray-300 transition-colors">Outlet</a>
|
||||
<a href="{{ route('product.index') }}" wire:navigate
|
||||
class="text-3xl font-bold text-white hover:text-gray-300 transition-colors">Produk</a>
|
||||
<a href="{{ route('voucher') }}"
|
||||
class="text-3xl font-bold text-white hover:text-gray-300 transition-colors cursor-pointer">Voucher</a>
|
||||
<a href="{{ route('article.index') }}"
|
||||
class="text-3xl font-bold text-white hover:text-gray-300 transition-colors">Artikel</a>
|
||||
<a href="{{ route('faq') }}"
|
||||
class="text-3xl font-bold text-white hover:text-gray-300 transition-colors">FAQ</a>
|
||||
@php
|
||||
$baseClass = 'text-3xl transition-colors';
|
||||
$activeClass = 'font-bold text-home-primary';
|
||||
$inactiveClass = 'text-white hover:text-gray-300 hover:opacity-80';
|
||||
@endphp
|
||||
|
||||
<a href="{{ route('homepage') }}" wire:navigate @click="isOpen = false"
|
||||
class="{{ $baseClass }} {{ request()->routeIs('homepage') ? $activeClass : $inactiveClass }}">
|
||||
Beranda
|
||||
</a>
|
||||
|
||||
<a href="{{ route('outlet') }}" wire:navigate @click="isOpen = false"
|
||||
class="{{ $baseClass }} {{ request()->routeIs('outlet') ? $activeClass : $inactiveClass }}">
|
||||
Outlet
|
||||
</a>
|
||||
|
||||
<a href="{{ route('product.index') }}" wire:navigate @click="isOpen = false"
|
||||
class="{{ $baseClass }} {{ request()->routeIs('product.*') ? $activeClass : $inactiveClass }}">
|
||||
Produk
|
||||
</a>
|
||||
|
||||
<a href="{{ route('voucher') }}" wire:navigate @click="isOpen = false"
|
||||
class="{{ $baseClass }} {{ request()->routeIs('voucher') ? $activeClass : $inactiveClass }}">
|
||||
Voucher
|
||||
</a>
|
||||
|
||||
<a href="{{ route('article.index') }}" wire:navigate @click="isOpen = false"
|
||||
class="{{ $baseClass }} {{ request()->routeIs('article.*') ? $activeClass : $inactiveClass }}">
|
||||
Artikel
|
||||
</a>
|
||||
|
||||
<a href="{{ route('faq') }}" wire:navigate @click="isOpen = false"
|
||||
class="{{ $baseClass }} {{ request()->routeIs('faq') ? $activeClass : $inactiveClass }}">
|
||||
FAQ
|
||||
</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user