diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 5cafd7c..f51bf3d 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useRef, useState } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import ThemeToggle from "@/components/ThemeToggle"; @@ -9,6 +9,7 @@ import NotificationCenter from "@/components/NotificationCenter"; import HeaderShortcutsButton from "@/components/HeaderShortcutsButton"; import NetworkStatus from "@/components/NetworkStatus"; import GlobalSearch from "@/components/GlobalSearch"; +import MobileSidebar from "@/components/layout/MobileSidebar"; const NAV_LINKS = [ { href: "/dashboard", label: "Dashboard" }, @@ -20,125 +21,93 @@ const NAV_LINKS = [ ]; export default function Navbar() { - const [menuOpen, setMenuOpen] = useState(false); + const [sidebarOpen, setSidebarOpen] = useState(false); const pathname = usePathname(); + const hamburgerRef = useRef(null); const isActive = (href: string) => pathname === href; return ( -
-
+ <> +
+
- {/* Logo */} - - {/* Star icon */} - - - StellarSplit - - + + + StellarSplit + + - {/* Desktop nav links */} - - - {/* Right-side actions */} -
-
- - - - - -
- - {/* New Invoice CTA */} - - New Invoice - - {/* Mobile menu toggle */} - + +
-
+
- {/* Mobile drawer */} - {menuOpen && ( - - )} - + {/* Slide-in sidebar for below lg */} + setSidebarOpen(false)} + triggerRef={hamburgerRef} + /> + ); } diff --git a/src/components/layout/MobileSidebar.tsx b/src/components/layout/MobileSidebar.tsx new file mode 100644 index 0000000..877c9d9 --- /dev/null +++ b/src/components/layout/MobileSidebar.tsx @@ -0,0 +1,146 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import FocusTrap from "@/components/FocusTrap"; + +const NAV_LINKS = [ + { href: "/dashboard", label: "Dashboard" }, + { href: "/subscriptions", label: "Subscriptions" }, + { href: "/groups", label: "Groups" }, + { href: "/address-book", label: "Contacts" }, + { href: "/recipients", label: "Recipients" }, + { href: "/leaderboard", label: "Leaderboard" }, +]; + +interface MobileSidebarProps { + isOpen: boolean; + onClose: () => void; + triggerRef: React.RefObject; +} + +export default function MobileSidebar({ isOpen, onClose, triggerRef }: MobileSidebarProps) { + const pathname = usePathname(); + + const isActive = (href: string) => pathname === href; + + // Close on Escape key + useEffect(() => { + if (!isOpen) return; + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + onClose(); + triggerRef.current?.focus(); + } + }; + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [isOpen, onClose, triggerRef]); + + // Prevent body scroll when open + useEffect(() => { + if (isOpen) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = ""; + } + return () => { + document.body.style.overflow = ""; + }; + }, [isOpen]); + + return ( + <> + {/* Backdrop */} +