fix: activeIndicator placement and animation
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navLinks {
|
.navLinks {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--space-xl);
|
gap: var(--space-xl);
|
||||||
@@ -69,12 +70,13 @@
|
|||||||
.activeIndicator {
|
.activeIndicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 50%;
|
left: 0;
|
||||||
transform: translateX(-50%);
|
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--md-sys-color-primary);
|
background-color: var(--md-sys-color-primary);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { motion } from 'motion/react';
|
import { motion } from 'motion/react';
|
||||||
import { useTranslation } from '../../i18n';
|
import { useTranslation } from '../../i18n';
|
||||||
@@ -9,6 +9,18 @@ export function Navbar() {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
|
const [indicatorX, setIndicatorX] = useState(0);
|
||||||
|
const navLinksRef = useRef<HTMLDivElement>(null);
|
||||||
|
const linkRefs = useRef<(HTMLAnchorElement | null)[]>([]);
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ path: '/', label: t.nav.home },
|
||||||
|
{ path: '/about', label: t.nav.about },
|
||||||
|
{ path: '/contact', label: t.nav.contact },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Find active index
|
||||||
|
const activeIndex = navLinks.findIndex((link) => link.path === location.pathname);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
@@ -22,11 +34,25 @@ export function Navbar() {
|
|||||||
setIsMobileMenuOpen(false);
|
setIsMobileMenuOpen(false);
|
||||||
}, [location.pathname]);
|
}, [location.pathname]);
|
||||||
|
|
||||||
const navLinks = [
|
// Calculate indicator position
|
||||||
{ path: '/', label: t.nav.home },
|
useEffect(() => {
|
||||||
{ path: '/about', label: t.nav.about },
|
const updateIndicatorPosition = () => {
|
||||||
{ path: '/contact', label: t.nav.contact },
|
const activeLink = linkRefs.current[activeIndex];
|
||||||
];
|
const container = navLinksRef.current;
|
||||||
|
|
||||||
|
if (activeLink && container) {
|
||||||
|
const containerRect = container.getBoundingClientRect();
|
||||||
|
const linkRect = activeLink.getBoundingClientRect();
|
||||||
|
// Calculate center of the link relative to the container
|
||||||
|
const centerX = linkRect.left - containerRect.left + linkRect.width / 2;
|
||||||
|
setIndicatorX(centerX);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateIndicatorPosition();
|
||||||
|
window.addEventListener('resize', updateIndicatorPosition);
|
||||||
|
return () => window.removeEventListener('resize', updateIndicatorPosition);
|
||||||
|
}, [activeIndex, language]); // Recalculate when active link or language changes
|
||||||
|
|
||||||
const toggleLanguage = () => {
|
const toggleLanguage = () => {
|
||||||
setLanguage(language === 'de' ? 'en' : 'de');
|
setLanguage(language === 'de' ? 'en' : 'de');
|
||||||
@@ -45,23 +71,27 @@ export function Navbar() {
|
|||||||
<span className={styles.logoAccent}>IT</span>
|
<span className={styles.logoAccent}>IT</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className={`${styles.navLinks} ${isMobileMenuOpen ? styles.open : ''}`}>
|
<div
|
||||||
{navLinks.map((link) => (
|
ref={navLinksRef}
|
||||||
|
className={`${styles.navLinks} ${isMobileMenuOpen ? styles.open : ''}`}
|
||||||
|
>
|
||||||
|
{navLinks.map((link, index) => (
|
||||||
<Link
|
<Link
|
||||||
key={link.path}
|
key={link.path}
|
||||||
|
ref={(el) => { linkRefs.current[index] = el; }}
|
||||||
to={link.path}
|
to={link.path}
|
||||||
className={`${styles.navLink} ${location.pathname === link.path ? styles.active : ''}`}
|
className={`${styles.navLink} ${location.pathname === link.path ? styles.active : ''}`}
|
||||||
>
|
>
|
||||||
{link.label}
|
{link.label}
|
||||||
{location.pathname === link.path && (
|
|
||||||
<motion.div
|
|
||||||
className={styles.activeIndicator}
|
|
||||||
layoutId="activeNav"
|
|
||||||
transition={{ type: 'spring', stiffness: 380, damping: 30 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
{activeIndex !== -1 && (
|
||||||
|
<motion.div
|
||||||
|
className={styles.activeIndicator}
|
||||||
|
animate={{ x: indicatorX }}
|
||||||
|
transition={{ type: 'spring', stiffness: 380, damping: 30 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
|
|||||||
Reference in New Issue
Block a user