Преглед изворни кода

✨ feat(header): adicionar menu hamburguer e destaque de secao ativa

joykepler пре 3 дана
родитељ
комит
203334de5f
1 измењених фајлова са 87 додато и 12 уклоњено
  1. 87 12
      app/components/AppHeader.vue

+ 87 - 12
app/components/AppHeader.vue

@@ -1,30 +1,105 @@
 <template>
-  <header class="absolute top-7 center-0 z-50 bg-black/60">
-    <nav class="w-full flex items-center justify-center h-[55px] px-4">
+  <header ref="headerRef" class="w-9/10 lg:w-auto absolute top-7 center-0 z-50 bg-black/80 lg:bg-black/60">
+    <nav class="flex items-center justify-center h-[55px] px-4 relative">
+      <div class="hidden md:flex items-center justify-center gap-1">
+        <NuxtLink
+          v-for="item in navItems"
+          :key="item.label"
+          :to="item.href"
+          class="relative text-xs font-open-sans font-semibold px-3 uppercase transition-colors duration-200 pb-1 leading-7"
+          :class="[
+            activeSection === item.href.slice(1)
+              ? 'text-[#E3FA6D] after:absolute after:bottom-0 after:left-0 after:w-full after:h-0.5 after:bg-[#E3FA6D]'
+              : 'text-white hover:text-white/85'
+          ]"
+        >
+          {{ item.label }}
+        </NuxtLink>
+      </div>
+
+      <NuxtLink to="#hero" class="md:hidden absolute left-4">
+        <NuxtImg src="/img/logo_2.png" class="h-9 w-auto object-contain" />
+      </NuxtLink>
+
+      <button
+        class="md:hidden absolute right-4 text-white focus:outline-none"
+        aria-label="Abrir menu"
+        @click.stop="menuOpen = !menuOpen"
+      >
+        <svg v-if="!menuOpen" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
+        </svg>
+        <svg v-else xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
+        </svg>
+      </button>
+    </nav>
+
+    <div
+      v-if="menuOpen"
+      class="md:hidden flex flex-col items-center py-4 gap-4"
+    >
       <NuxtLink
         v-for="item in navItems"
         :key="item.label"
         :to="item.href"
-        class="relative text-xs font-open-sans font-semibold px-3 uppercase transition-colors duration-200 pb-1 leading-7"
+        class="relative text-xs font-open-sans font-semibold uppercase transition-colors duration-200"
         :class="[
-          item.active
-            ? 'text-[#E3FA6D] after:absolute after:bottom-0 after:left-0 after:w-full after:h-0.5 after:bg-[#E3FA6D]'
+          activeSection === item.href.slice(1)
+            ? 'text-[#E3FA6D]'
             : 'text-white hover:text-white/85'
         ]"
+        @click="menuOpen = false"
       >
         {{ item.label }}
       </NuxtLink>
-    </nav>
+    </div>
   </header>
 </template>
 
 <script setup lang="ts">
+const menuOpen = ref(false)
+const headerRef = ref<HTMLElement | null>(null)
+const activeSection = ref('hero')
+
 const navItems = [
-  { label: 'Home', href: '#hero', active: true },
-  { label: 'A Imersão', href: '#imersao', active: false },
-  { label: 'Metodologia', href: '#metodologia', active: false },
-  { label: 'Executores', href: '#executores', active: false },
-  { label: 'Próximos Destinos', href: '#destinos', active: false },
-  { label: 'Contato', href: '#contato', active: false },
+  { label: 'Home', href: '#hero' },
+  { label: 'A Imersão', href: '#imersao' },
+  { label: 'Metodologia', href: '#metodologia' },
+  { label: 'Executores', href: '#executores' },
+  { label: 'Próximos Destinos', href: '#destinos' },
+  { label: 'Contato', href: '#contato' },
 ]
+
+function handleClickOutside(event: MouseEvent) {
+  if (menuOpen.value && headerRef.value && !headerRef.value.contains(event.target as Node)) {
+    menuOpen.value = false
+  }
+}
+
+onMounted(() => {
+  document.addEventListener('click', handleClickOutside)
+
+  const sectionIds = navItems.map(i => i.href.slice(1))
+  const observer = new IntersectionObserver(
+    (entries) => {
+      for (const entry of entries) {
+        if (entry.isIntersecting) {
+          activeSection.value = entry.target.id
+        }
+      }
+    },
+    { threshold: 0.3 }
+  )
+
+  sectionIds.forEach(id => {
+    const el = document.getElementById(id)
+    if (el) observer.observe(el)
+  })
+
+  onUnmounted(() => {
+    document.removeEventListener('click', handleClickOutside)
+    observer.disconnect()
+  })
+})
 </script>