277 lines
7.8 KiB
Vue
277 lines
7.8 KiB
Vue
<script setup lang="ts">
|
||
import type { Home, Setting, MainMenu } from "~/types";
|
||
|
||
interface Props {
|
||
home?: Home | null;
|
||
setting?: Setting | null;
|
||
menu?: MainMenu | null;
|
||
}
|
||
|
||
defineProps<Props>();
|
||
|
||
const config = useRuntimeConfig()
|
||
const apiUrl = computed(() => config.public.BASE_API_URL || 'http://127.0.0.1:8000')
|
||
|
||
|
||
// Helper function to get full image URL
|
||
const getImageUrl = (imagePath: string | null | undefined) => {
|
||
if (!imagePath) return '/assets/images/about3.jpg'
|
||
if (imagePath.startsWith('http')) return imagePath
|
||
return `${apiUrl.value}${imagePath}`
|
||
}
|
||
|
||
// Current active section tracking
|
||
const route = useRoute()
|
||
|
||
// Initialize currentSection based on route hash (works on both server and client)
|
||
const initializeCurrentSection = () => {
|
||
if (route.hash) {
|
||
return route.hash.replace('#', '')
|
||
}
|
||
return 'home'
|
||
}
|
||
|
||
const currentSection = ref(initializeCurrentSection())
|
||
|
||
// Handle hash navigation for menu items
|
||
const handleHashNavigation = (event: Event, hash: string) => {
|
||
const route = useRoute()
|
||
|
||
// If we're not on the home page, navigate to home first
|
||
if (route.path !== '/') {
|
||
event.preventDefault()
|
||
navigateTo('/' + hash)
|
||
return
|
||
}
|
||
|
||
// On home page, handle smooth scroll
|
||
event.preventDefault()
|
||
|
||
if (import.meta.client) {
|
||
const targetId = hash.replace('#', '')
|
||
|
||
// Handle home section - scroll to top
|
||
if (targetId === 'home') {
|
||
currentSection.value = 'home'
|
||
window.scrollTo({
|
||
top: 0,
|
||
behavior: 'smooth'
|
||
})
|
||
window.history.pushState(null, '', '/')
|
||
return
|
||
}
|
||
|
||
// Handle other sections
|
||
const targetElement = document.getElementById(targetId)
|
||
|
||
if (targetElement) {
|
||
// Update current section
|
||
currentSection.value = targetId
|
||
|
||
// Smooth scroll to element
|
||
targetElement.scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'start'
|
||
})
|
||
|
||
// Update URL hash without page jump
|
||
window.history.pushState(null, '', hash)
|
||
}
|
||
}
|
||
}
|
||
|
||
// Check if menu item is active
|
||
const isMenuActive = (path: string) => {
|
||
// Extract hash from path (e.g., "/#about" -> "about")
|
||
const pathHash = path.includes('#') ? path.split('#')[1] : ''
|
||
|
||
if (import.meta.server) {
|
||
// Server-side: URL hash kontrolü
|
||
if (path === '/') {
|
||
// Home is active only if there's no hash
|
||
return route.path === '/' && !route.hash
|
||
}
|
||
// Other sections: check if route hash matches
|
||
const routeHash = route.hash.replace('#', '')
|
||
return routeHash === pathHash
|
||
}
|
||
|
||
// Client-side: currentSection state kullan
|
||
if (path === '/') {
|
||
return currentSection.value === '' || currentSection.value === 'home'
|
||
}
|
||
|
||
return currentSection.value === pathHash
|
||
}
|
||
|
||
// Update current section based on scroll
|
||
const updateCurrentSection = () => {
|
||
if (import.meta.client) {
|
||
const sections = ['home', 'about', 'service', 'resume', 'project-gallery', 'contact']
|
||
const scrollPosition = window.scrollY + 100
|
||
|
||
for (const section of sections) {
|
||
const element = document.getElementById(section)
|
||
if (element) {
|
||
const { offsetTop, offsetHeight } = element
|
||
if (scrollPosition >= offsetTop && scrollPosition < offsetTop + offsetHeight) {
|
||
currentSection.value = section
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
// Default to home if no section matched
|
||
if (scrollPosition < 100) {
|
||
currentSection.value = 'home'
|
||
}
|
||
}
|
||
}
|
||
|
||
// Reinitialize menu on mount and route change
|
||
onMounted(() => {
|
||
// Add scroll listener
|
||
window.addEventListener('scroll', updateCurrentSection)
|
||
|
||
// Wait for DOM to be fully ready
|
||
nextTick(() => {
|
||
setTimeout(() => {
|
||
initializeMenu()
|
||
reinitWow()
|
||
updateCurrentSection()
|
||
}, 100)
|
||
})
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
if (import.meta.client) {
|
||
window.removeEventListener('scroll', updateCurrentSection)
|
||
}
|
||
})
|
||
|
||
watch(() => route.path, () => {
|
||
nextTick(() => {
|
||
setTimeout(() => {
|
||
initializeMenu()
|
||
reinitWow()
|
||
updateCurrentSection()
|
||
}, 100)
|
||
})
|
||
})
|
||
|
||
// Also watch for hash changes
|
||
watch(() => route.hash, (newHash) => {
|
||
if (newHash) {
|
||
currentSection.value = newHash.replace('#', '')
|
||
} else {
|
||
currentSection.value = 'home'
|
||
}
|
||
|
||
nextTick(() => {
|
||
setTimeout(() => {
|
||
initializeMenu()
|
||
}, 100)
|
||
})
|
||
})
|
||
|
||
const initializeMenu = () => {
|
||
if (import.meta.client && (window as any).$) {
|
||
// Disable jQuery onePageNav plugin completely - we handle everything with Vue
|
||
const $mainmenu = (window as any).$('#mainmenu-area')
|
||
if ($mainmenu.length) {
|
||
// Remove any existing onePageNav bindings
|
||
$mainmenu.off('click.onePageNav')
|
||
// Don't initialize onePageNav at all - Vue handles everything
|
||
}
|
||
}
|
||
}
|
||
|
||
const reinitWow = () => {
|
||
if (import.meta.client && (window as any).WOW) {
|
||
// Only reinitialize WOW for menu items, not profile area
|
||
const menuItems = document.querySelectorAll('#mainmenu-area li a.wow')
|
||
menuItems.forEach((item) => {
|
||
item.classList.remove('animated', 'fadeInUp')
|
||
// Force reflow
|
||
void (item as HTMLElement).offsetWidth
|
||
item.classList.add('animated', 'fadeInUp')
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="side-menu-wrapper">
|
||
<div class="menu-toogle-icon">
|
||
<div id="nav-icon3">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</div>
|
||
</div>
|
||
<div class="side-menu">
|
||
<div class="heading-area">
|
||
<a href="/" class="profile-photo">
|
||
<img :src="getImageUrl(home?.image)" :alt="home?.name || 'Profile'">
|
||
</a>
|
||
<div class="name">
|
||
{{ home?.name || 'Atahan Deniz' }}
|
||
</div>
|
||
</div>
|
||
<ul id="mainmenu-area">
|
||
<li :class="{ current: isMenuActive('/') }">
|
||
<NuxtLink to="/" @click="handleHashNavigation($event, '#home')" class="wow fadeInUp" data-wow-delay="0.4s">
|
||
<i class="fas fa-home"></i> {{ menu?.home || 'Anasayfa' }}
|
||
</NuxtLink>
|
||
</li>
|
||
<li :class="{ current: isMenuActive('/#about') }">
|
||
<NuxtLink to="/#about" @click="handleHashNavigation($event, '#about')" class="wow fadeInUp" data-wow-delay="0.4s">
|
||
<i class="fas fa-user"></i> {{ menu?.about || 'Hakkımda' }}
|
||
</NuxtLink>
|
||
</li>
|
||
<li :class="{ current: isMenuActive('/#service') }">
|
||
<NuxtLink to="/#service" @click="handleHashNavigation($event, '#service')" class="wow fadeInUp" data-wow-delay="0.4s">
|
||
<i class="fas fa-briefcase"></i> {{ menu?.services || 'Hizmetlerim' }}
|
||
</NuxtLink>
|
||
</li>
|
||
<li :class="{ current: isMenuActive('/#resume') }">
|
||
<NuxtLink to="/#resume" @click="handleHashNavigation($event, '#resume')" class="wow fadeInUp" data-wow-delay="0.4s">
|
||
<i class="fas fa-file-alt"></i> {{ menu?.resume || 'Becerilerim' }}
|
||
</NuxtLink>
|
||
</li>
|
||
<li :class="{ current: isMenuActive('/#project-gallery') }">
|
||
<NuxtLink to="/#project-gallery" @click="handleHashNavigation($event, '#project-gallery')" class="wow fadeInUp" data-wow-delay="0.4s">
|
||
<i class="fas fa-layer-group"></i> {{ menu?.portfolio || 'Portfolio' }}
|
||
</NuxtLink>
|
||
</li>
|
||
<li :class="{ current: isMenuActive('/#contact') }">
|
||
<NuxtLink to="/#contact" @click="handleHashNavigation($event, '#contact')" class="wow fadeInUp" data-wow-delay="0.4s">
|
||
<i class="fas fa-check-square"></i> {{ menu?.contact || 'İletişim' }}
|
||
</NuxtLink>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* Ensure menu items are always visible */
|
||
#mainmenu-area li {
|
||
opacity: 1 !important;
|
||
visibility: visible !important;
|
||
}
|
||
|
||
#mainmenu-area li a {
|
||
opacity: 1 !important;
|
||
visibility: visible !important;
|
||
}
|
||
|
||
/* Ensure profile photo and name are always visible */
|
||
.heading-area .profile-photo,
|
||
.heading-area .profile-photo img,
|
||
.heading-area .name {
|
||
opacity: 1 !important;
|
||
visibility: visible !important;
|
||
}
|
||
</style> |