fixed burger menu mobile

This commit is contained in:
kinou-p 2025-10-02 15:57:10 +02:00
parent 009ea87198
commit 486451381c
3 changed files with 100 additions and 13 deletions

View File

@ -2,7 +2,7 @@ import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";
import ProjectPage from "./pages/ProjectPage";
@ -13,6 +13,21 @@ const queryClient = new QueryClient();
const IndexWrapper = () => <Index />;
const router = createBrowserRouter([
{
path: "/",
element: <IndexWrapper />,
},
{
path: "/project/:projectId",
element: <ProjectPage />,
},
{
path: "*",
element: <NotFound />,
},
]);
const App = () => (
<QueryClientProvider client={queryClient}>
<TooltipProvider>
@ -20,14 +35,7 @@ const App = () => (
<ParticlesBackground />
<Toaster />
<Sonner />
<BrowserRouter>
<Routes>
<Route path="/" element={<IndexWrapper />} />
<Route path="/project/:projectId" element={<ProjectPage />} />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
<RouterProvider router={router} />
</ThemeProvider>
</TooltipProvider>
</QueryClientProvider>

View File

@ -1,26 +1,53 @@
import { Moon, Sun } from "lucide-react";
import { Moon, Sun, Menu, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useTheme } from "@/contexts/ThemeContext";
import { useLanguage } from "@/contexts/LanguageContext";
import { motion } from "framer-motion";
import { motion, AnimatePresence } from "framer-motion";
import { useLocation, useNavigate } from "react-router-dom";
import { useState, useEffect, useRef } from "react";
export const Header = () => {
const { theme, toggleTheme } = useTheme();
const { language, setLanguage, t } = useLanguage();
const location = useLocation();
const navigate = useNavigate();
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const mobileMenuRef = useRef<HTMLDivElement>(null);
// Check if we're on a project page
const isProjectPage = location.pathname.startsWith('/project/');
// Close mobile menu when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (mobileMenuRef.current && !mobileMenuRef.current.contains(event.target as Node)) {
setMobileMenuOpen(false);
}
};
if (mobileMenuOpen) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [mobileMenuOpen]);
const handleNavigation = (section: string) => {
// Close mobile menu immediately
setMobileMenuOpen(false);
// Then scroll to section (happens independently)
if (isProjectPage) {
// If on project page, navigate to home with section anchor
navigate(`/#${section}`);
} else {
// If on home page, scroll to section
scrollToSection(section);
// Use setTimeout to ensure scroll happens after menu animation starts
setTimeout(() => {
scrollToSection(section);
}, 100);
}
};
@ -50,6 +77,7 @@ export const Header = () => {
initial={{ y: -100 }}
animate={{ y: 0 }}
className="fixed top-0 left-0 right-0 z-50 border-b border-border/40 bg-background/80 backdrop-blur-lg"
ref={mobileMenuRef}
>
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
<motion.button
@ -63,6 +91,7 @@ export const Header = () => {
AP
</motion.button>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center gap-8">
{["home", "projects", "skills", "contact"].map((item, i) => (
<motion.button
@ -107,8 +136,58 @@ export const Header = () => {
)}
</Button>
</motion.div>
{/* Mobile Menu Button */}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.5 }}
className="md:hidden"
>
<Button
variant="ghost"
size="icon"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
aria-label="Toggle menu"
>
{mobileMenuOpen ? (
<X className="h-5 w-5" />
) : (
<Menu className="h-5 w-5" />
)}
</Button>
</motion.div>
</div>
</div>
{/* Mobile Navigation */}
<AnimatePresence>
{mobileMenuOpen && (
<motion.nav
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3 }}
className="md:hidden border-t border-border/40 bg-background/95 backdrop-blur-lg overflow-hidden"
>
<div className="container mx-auto px-4 py-4 flex flex-col gap-4">
{["home", "projects", "skills", "contact"].map((item, i) => (
<motion.button
key={item}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ delay: 0.1 * i }}
onClick={() => handleNavigation(item)}
className="text-left text-base font-medium text-muted-foreground hover:text-foreground transition-colors py-2"
>
{t(`nav.${item}`)}
</motion.button>
))}
</div>
</motion.nav>
)}
</AnimatePresence>
</motion.header>
);
};

View File

@ -16,7 +16,7 @@ export const ParticlesBackground = () => {
}, []);
const particlesLoaded = async (container) => {
console.log(container);
// console.log(container);
};
const options = useMemo(