Merge pull request #2 from AshwaniNITR/Shoaib's-branch

extension of resources navbar
This commit is contained in:
Shoaib1M
2025-10-18 12:33:08 +05:30
committed by GitHub
3 changed files with 115 additions and 31 deletions

3
src/app/books/page.jsx Normal file
View File

@@ -0,0 +1,3 @@
export default function BooksPage() {
return <h1 className="text-center text-black mt-20">Books Page</h1>;
}

View File

@@ -3,7 +3,6 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
// Animation variants
const ANIMATIONS = { const ANIMATIONS = {
overlay: { overlay: {
hidden: { opacity: 0 }, hidden: { opacity: 0 },
@@ -30,15 +29,14 @@ const ANIMATIONS = {
export default function Navbar() { export default function Navbar() {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [showDropdown, setShowDropdown] = useState(false);
// Close on Esc
useEffect(() => { useEffect(() => {
const onKey = (e) => e.key === "Escape" && setIsOpen(false); const onKey = (e) => e.key === "Escape" && setIsOpen(false);
window.addEventListener("keydown", onKey); window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey);
}, []); }, []);
// Lock body scroll when open
useEffect(() => { useEffect(() => {
document.documentElement.classList.toggle("overflow-hidden", isOpen); document.documentElement.classList.toggle("overflow-hidden", isOpen);
return () => document.documentElement.classList.remove("overflow-hidden"); return () => document.documentElement.classList.remove("overflow-hidden");
@@ -46,6 +44,15 @@ export default function Navbar() {
const toggleMenu = () => setIsOpen((v) => !v); const toggleMenu = () => setIsOpen((v) => !v);
const mainLinks = [
"Home",
"Events",
"Projects",
"Team",
"Resources",
"Contact",
];
return ( return (
<> <>
{/* ---------------- MOBILE NAV ---------------- */} {/* ---------------- MOBILE NAV ---------------- */}
@@ -54,7 +61,7 @@ export default function Navbar() {
{/* Logo */} {/* Logo */}
<div className="text-blue-500 text-2xl font-bold">ML4E</div> <div className="text-blue-500 text-2xl font-bold">ML4E</div>
{/* Hamburger / Cross button (moves to top-right & higher z when open) */} {/* Hamburger Button */}
<button <button
onClick={toggleMenu} onClick={toggleMenu}
aria-label="Toggle menu" aria-label="Toggle menu"
@@ -84,7 +91,6 @@ export default function Navbar() {
<AnimatePresence> <AnimatePresence>
{isOpen && ( {isOpen && (
<> <>
{/* Dark overlay behind button */}
<motion.div <motion.div
className="fixed inset-0 bg-gray-900/95 z-40" className="fixed inset-0 bg-gray-900/95 z-40"
variants={ANIMATIONS.overlay} variants={ANIMATIONS.overlay}
@@ -93,7 +99,6 @@ export default function Navbar() {
exit="exit" exit="exit"
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
/> />
{/* Menu layer (below the floating button) */}
<motion.div <motion.div
className="fixed inset-0 z-50 flex flex-col justify-center items-center" className="fixed inset-0 z-50 flex flex-col justify-center items-center"
variants={ANIMATIONS.menuContainer} variants={ANIMATIONS.menuContainer}
@@ -105,8 +110,40 @@ export default function Navbar() {
className="flex flex-col items-center gap-6 px-4" className="flex flex-col items-center gap-6 px-4"
variants={ANIMATIONS.menuContainer} variants={ANIMATIONS.menuContainer}
> >
{["Home", "Events", "Projects", "Team", "Resources", "Contact"].map( {mainLinks.map((item) => {
(item) => ( if (item === "Resources") {
return (
<div
key="resources"
className="flex flex-col items-center"
>
<motion.button
onClick={() => setShowDropdown(!showDropdown)}
className="text-xl text-white hover:text-blue-300 transition-colors py-2 px-4 rounded-lg hover:bg-white/5"
>
Resources
</motion.button>
{showDropdown && (
<div className="flex flex-col items-center gap-2 mt-2">
<a
href="/onlineresources"
className="text-white text-base hover:text-blue-300"
>
Online Resources
</a>
<a
href="/books"
className="text-white text-base hover:text-blue-300"
>
Books
</a>
</div>
)}
</div>
);
}
return (
<motion.a <motion.a
key={item} key={item}
href={`#${item.toLowerCase()}`} href={`#${item.toLowerCase()}`}
@@ -116,8 +153,8 @@ export default function Navbar() {
> >
{item} {item}
</motion.a> </motion.a>
) );
)} })}
</motion.div> </motion.div>
</motion.div> </motion.div>
</> </>
@@ -125,7 +162,7 @@ export default function Navbar() {
</AnimatePresence> </AnimatePresence>
</nav> </nav>
{/* ---------------- DESKTOP SIDEBAR ---------------- */} {/* ---------------- DESKTOP NAV ---------------- */}
<div className="hidden md:block"> <div className="hidden md:block">
<div <div
className={`fixed top-0 left-0 h-screen w-20 flex flex-col justify-between items-center py-6 z-50 className={`fixed top-0 left-0 h-screen w-20 flex flex-col justify-between items-center py-6 z-50
@@ -138,8 +175,7 @@ export default function Navbar() {
{/* Logo */} {/* Logo */}
<div className="text-white text-2xl font-bold">ML4E</div> <div className="text-white text-2xl font-bold">ML4E</div>
{/* Desktop hamburger (does NOT turn into cross; cross is at top-right) */} {!isOpen && (
{!isOpen?(
<button <button
onClick={toggleMenu} onClick={toggleMenu}
aria-label="Open menu" aria-label="Open menu"
@@ -150,8 +186,7 @@ export default function Navbar() {
<span className="block w-6 h-0.5 bg-blue-400" /> <span className="block w-6 h-0.5 bg-blue-400" />
<span className="block w-6 h-0.5 bg-blue-400 mt-1.5" /> <span className="block w-6 h-0.5 bg-blue-400 mt-1.5" />
</button> </button>
):(null)} )}
<div className="h-6" /> <div className="h-6" />
</div> </div>
@@ -176,7 +211,6 @@ export default function Navbar() {
animate="visible" animate="visible"
exit="exit" exit="exit"
> >
{/* Desktop cross at top-right only */}
<button <button
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
aria-label="Close menu" aria-label="Close menu"
@@ -189,19 +223,61 @@ export default function Navbar() {
className="flex flex-col md:flex-row md:flex-wrap items-center justify-center gap-6 max-w-4xl px-4" className="flex flex-col md:flex-row md:flex-wrap items-center justify-center gap-6 max-w-4xl px-4"
variants={ANIMATIONS.menuContainer} variants={ANIMATIONS.menuContainer}
> >
{["Home", "Events", "Projects", "Team", "Resources", "Contact"].map( {mainLinks.map((item) => {
(item) => ( if (item === "Resources") {
return (
<div
key="resources-desktop"
className="relative group"
onMouseEnter={() => setShowDropdown(true)}
onMouseLeave={() => setShowDropdown(false)}
>
<motion.span
className="text-2xl text-white hover:text-blue-300 transition-colors py-2 px-6 rounded-lg hover:bg-white/5"
variants={ANIMATIONS.menuItem}
>
Resources
</motion.span>
<AnimatePresence>
{showDropdown && (
<motion.div
className="absolute top-full left-0 mt-2 flex flex-col bg-white/10 border border-white/20 rounded-lg shadow-lg"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
>
<a
href="/onlineresources"
className="px-4 py-2 text-white hover:bg-blue-500/20"
>
Online Resources
</a>
<a
href="/books"
className="px-4 py-2 text-white hover:bg-blue-500/20"
>
Books
</a>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
return (
<motion.a <motion.a
key={item} key={item}
href={`#${item.toLowerCase()}`} href={`#${item.toLowerCase()}`}
variants={ANIMATIONS.menuItem} variants={ANIMATIONS.menuItem}
className="text-xl md:text-2xl text-white hover:text-blue-300 transition-colors py-2 px-6 rounded-lg hover:bg-white/5" className="text-2xl text-white hover:text-blue-300 transition-colors py-2 px-6 rounded-lg hover:bg-white/5"
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
> >
{item} {item}
</motion.a> </motion.a>
) );
)} })}
</motion.div> </motion.div>
</motion.div> </motion.div>
</> </>

View File

@@ -0,0 +1,5 @@
export default function OnlineResourcesPage() {
return (
<h1 className="text-center text-black mt-20">Online Resources Page</h1>
);
}