Improve nav item click handling and sidebar menu close
Enhanced NavItem to prevent double-clicks, handle active state, and optimize mobile menu closing. Sidebar now uses a dedicated callback for closing the mobile menu, reducing unnecessary re-renders and improving user experience.
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { useRef } from "react"
|
||||
import type { LucideIcon } from "lucide-react"
|
||||
import type React from "react"
|
||||
import { cn } from "@/lib/utils/styles"
|
||||
|
||||
interface NavItemProps {
|
||||
href: string
|
||||
@@ -9,14 +14,57 @@ interface NavItemProps {
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
export const NavItem: React.FC<NavItemProps> = ({ href, icon: Icon, children, onClick }) => (
|
||||
<Link
|
||||
href={href}
|
||||
onClick={onClick}
|
||||
className="flex items-center px-3 py-2 text-sm rounded-md transition-colors text-muted-foreground hover:text-foreground hover:bg-accent"
|
||||
>
|
||||
<Icon className="h-4 w-4 mr-3 flex-shrink-0" />
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
export const NavItem: React.FC<NavItemProps> = ({ href, icon: Icon, children, onClick }) => {
|
||||
const pathname = usePathname()
|
||||
const isActive = pathname === href || (href !== '/dashboard' && pathname?.startsWith(href))
|
||||
const isNavigatingRef = useRef(false)
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
// Prevent rapid double-clicks
|
||||
if (isNavigatingRef.current) {
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
// If already on this page, just close mobile menu if needed
|
||||
if (isActive) {
|
||||
e.preventDefault()
|
||||
if (onClick) onClick()
|
||||
return
|
||||
}
|
||||
|
||||
// Mark as navigating to prevent double-clicks
|
||||
isNavigatingRef.current = true
|
||||
|
||||
// Call onClick handler (for mobile menu closing) - don't block navigation
|
||||
if (onClick) {
|
||||
// Use setTimeout to ensure navigation happens first
|
||||
setTimeout(() => onClick(), 0)
|
||||
}
|
||||
|
||||
// Reset flag after navigation completes
|
||||
setTimeout(() => {
|
||||
isNavigatingRef.current = false
|
||||
}, 300)
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
"flex items-center px-3 py-2 text-sm rounded-md transition-colors",
|
||||
"text-muted-foreground hover:text-foreground hover:bg-accent",
|
||||
"active:scale-[0.98] transition-transform duration-75",
|
||||
"touch-manipulation will-change-transform",
|
||||
isActive && "bg-accent text-foreground font-medium"
|
||||
)}
|
||||
prefetch={true}
|
||||
style={{ WebkitTapHighlightColor: 'transparent' }}
|
||||
>
|
||||
<Icon className="h-4 w-4 mr-3 flex-shrink-0" />
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user