fix: align navbar profile button styling with other nav links
This commit is contained in:
+188
-174
@@ -18,6 +18,7 @@ export default function Navbar() {
|
|||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
|
||||||
const isLanding = pathname === "/"
|
const isLanding = pathname === "/"
|
||||||
|
const isAuthRoute = pathname.startsWith("/login") || pathname.startsWith("/signup") || pathname.startsWith("/forgot-password") || pathname.startsWith("/reset-password")
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useState(
|
const [searchQuery, setSearchQuery] = useState(
|
||||||
() => searchParams.get("q") || "",
|
() => searchParams.get("q") || "",
|
||||||
@@ -117,193 +118,206 @@ export default function Navbar() {
|
|||||||
(route) => route.href !== "/" && route.href !== "/search",
|
(route) => route.href !== "/" && route.href !== "/search",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Hide navbar on auth routes
|
||||||
|
if (isAuthRoute) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<motion.nav
|
<AnimatePresence>
|
||||||
initial={{ opacity: 0, y: "-100%" }}
|
<motion.nav
|
||||||
animate={{ opacity: 1, y: 0 }}
|
key='navbar'
|
||||||
className='w-full flex flex-row px-4 md:px-[10svw] py-2 items-center gap-4 md:gap-8 justify-between border-b border-border sticky top-0 z-50 backdrop-blur-sm bg-background/80'
|
initial={{ opacity: 0, y: "-100%" }}
|
||||||
>
|
animate={{ opacity: 1, y: 0 }}
|
||||||
<Link
|
exit={{ opacity: 0, y: "-100%" }}
|
||||||
href='/'
|
className='w-full flex flex-row px-4 md:px-[10svw] py-2 items-center gap-4 md:gap-8 justify-between border-b border-border sticky top-0 z-50 backdrop-blur-sm bg-background/80'
|
||||||
className='flex flex-row gap-2 items-center font-bold text-lg py-1 shrink-0'
|
|
||||||
>
|
>
|
||||||
<Image
|
<Link
|
||||||
src={logo}
|
href='/'
|
||||||
alt='DeckyVault Logo'
|
className='flex flex-row gap-2 items-center font-bold text-lg py-1 shrink-0'
|
||||||
className='h-6 my-1 w-auto'
|
>
|
||||||
/>
|
<Image
|
||||||
{!isLanding && (
|
src={logo}
|
||||||
<motion.span className='hidden md:inline-block'>
|
alt='DeckyVault Logo'
|
||||||
DeckyVault
|
className='h-6 my-1 w-auto'
|
||||||
</motion.span>
|
/>
|
||||||
)}
|
{!isLanding && (
|
||||||
</Link>
|
<motion.span className='hidden md:inline-block'>
|
||||||
|
DeckyVault
|
||||||
|
</motion.span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{!isLanding && (
|
{!isLanding && (
|
||||||
<motion.label
|
<motion.label
|
||||||
key='search-bar'
|
key='search-bar'
|
||||||
layoutId='search-bar'
|
layoutId='search-bar'
|
||||||
initial={{ opacity: 0, scale: 0.95 }}
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
exit={{ opacity: 0, scale: 0.95 }}
|
exit={{ opacity: 0, scale: 0.95 }}
|
||||||
transition={{ duration: 0.3 }}
|
transition={{ duration: 0.3 }}
|
||||||
className={`flex flex-row items-center gap-2 flex-1 bg-text/5 px-2 py-1 rounded-md border placeholder:text-text/60 group transition-colors cursor-text ${
|
className={`flex flex-row items-center gap-2 flex-1 bg-text/5 px-2 py-1 rounded-md border placeholder:text-text/60 group transition-colors cursor-text ${
|
||||||
isFocused || forceFocusStyles
|
|
||||||
? "border-primary/80 ring-2 ring-primary/50 ring-offset-2 ring-offset-background"
|
|
||||||
: "border-border hover:border-border-active"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Gamepad2Icon
|
|
||||||
className={`h-4 w-4 transition-colors shrink-0 ${
|
|
||||||
isFocused || forceFocusStyles
|
isFocused || forceFocusStyles
|
||||||
? "stroke-accent"
|
? "border-primary/80 ring-2 ring-primary/50 ring-offset-2 ring-offset-background"
|
||||||
: ""
|
: "border-border hover:border-border-active"
|
||||||
}`}
|
}`}
|
||||||
/>
|
>
|
||||||
<input
|
<Gamepad2Icon
|
||||||
ref={inputRef}
|
className={`h-4 w-4 transition-colors shrink-0 ${
|
||||||
type='text'
|
|
||||||
placeholder='search by game or appid...'
|
|
||||||
value={searchQuery}
|
|
||||||
onChange={handleSearchChange}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
onFocus={() => setIsFocused(true)}
|
|
||||||
onBlur={() => setIsFocused(false)}
|
|
||||||
className='flex-1 outline-none bg-transparent text-sm min-w-0'
|
|
||||||
/>
|
|
||||||
<motion.button>
|
|
||||||
<CircleXIcon
|
|
||||||
onClick={() => {
|
|
||||||
setSearchQuery("")
|
|
||||||
if (!isLanding) {
|
|
||||||
const params = new URLSearchParams(
|
|
||||||
searchParams.toString(),
|
|
||||||
)
|
|
||||||
params.delete("q")
|
|
||||||
router.replace(
|
|
||||||
`/search?${params.toString()}`,
|
|
||||||
{
|
|
||||||
scroll: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={`h-3 w-3 transition-color cursor-default hover:stroke-accent transition-all ${
|
|
||||||
isFocused || forceFocusStyles
|
isFocused || forceFocusStyles
|
||||||
? "opacity-100"
|
? "stroke-accent"
|
||||||
: "opacity-0"
|
: ""
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</motion.button>
|
<input
|
||||||
</motion.label>
|
ref={inputRef}
|
||||||
)}
|
type='text'
|
||||||
</AnimatePresence>
|
placeholder='search by game or appid...'
|
||||||
|
value={searchQuery}
|
||||||
{/* Desktop Navigation Links */}
|
onChange={handleSearchChange}
|
||||||
<ul className='hidden md:flex flex-row items-center gap-6 shrink-0'>
|
onKeyDown={handleKeyDown}
|
||||||
{navbarRoutes.map((route) => (
|
onFocus={() => setIsFocused(true)}
|
||||||
<Link
|
onBlur={() => setIsFocused(false)}
|
||||||
key={route.href}
|
className='flex-1 outline-none bg-transparent text-sm min-w-0'
|
||||||
href={route.href}
|
/>
|
||||||
title={`Navigate to ${route.title}`}
|
<motion.button>
|
||||||
className='text-sm font-medium hover:text-primary transition-colors uppercase'
|
<CircleXIcon
|
||||||
>
|
onClick={() => {
|
||||||
{route.title}
|
setSearchQuery("")
|
||||||
</Link>
|
if (!isLanding) {
|
||||||
))}
|
const params =
|
||||||
<AnimatePresence mode='popLayout' initial={false}>
|
new URLSearchParams(
|
||||||
{isSessionLoading ? (
|
searchParams.toString(),
|
||||||
<motion.li
|
)
|
||||||
key='loading-placeholder'
|
params.delete("q")
|
||||||
initial={{ opacity: 0 }}
|
router.replace(
|
||||||
animate={{ opacity: 1 }}
|
`/search?${params.toString()}`,
|
||||||
exit={{ opacity: 0 }}
|
{
|
||||||
className='w-20 h-8 rounded-lg bg-white/3 animate-pulse'
|
scroll: false,
|
||||||
/>
|
},
|
||||||
) : session ? (
|
)
|
||||||
<motion.li
|
|
||||||
key='dashboard-link'
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
exit={{ opacity: 0 }}
|
|
||||||
className='relative'
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
setUserMenuOpen(!userMenuOpen)
|
|
||||||
}
|
|
||||||
className='flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-white/5 transition-colors'
|
|
||||||
>
|
|
||||||
{session.user.image ? (
|
|
||||||
<Image
|
|
||||||
src={session.user.image}
|
|
||||||
alt=''
|
|
||||||
width={28}
|
|
||||||
height={28}
|
|
||||||
unoptimized
|
|
||||||
className='h-7 w-7 rounded-full'
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className='h-7 w-7 rounded-full bg-secondary flex items-center justify-center'>
|
|
||||||
<User className='h-3.5 w-3.5 text-text' />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<span className='text-sm text-text max-w-25 truncate'>
|
|
||||||
{session.user.name}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{userMenuOpen && (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className='fixed inset-0 z-40'
|
|
||||||
onClick={() =>
|
|
||||||
setUserMenuOpen(false)
|
|
||||||
}
|
}
|
||||||
/>
|
}}
|
||||||
<div className='absolute right-0 top-full mt-1 w-48 bg-[#1a1020] border border-white/10 rounded-lg shadow-lg z-50 py-1'>
|
className={`h-3 w-3 transition-color cursor-default hover:stroke-accent transition-all ${
|
||||||
<button
|
isFocused || forceFocusStyles
|
||||||
onClick={async () => {
|
? "opacity-100"
|
||||||
setUserMenuOpen(false)
|
: "opacity-0"
|
||||||
await authClient.signOut()
|
}`}
|
||||||
}}
|
/>
|
||||||
className='w-full flex items-center gap-2 px-3 py-2 text-sm text-text/70 hover:text-text hover:bg-text/5 transition-colors'
|
</motion.button>
|
||||||
>
|
</motion.label>
|
||||||
<LogOut className='h-4 w-4' />
|
|
||||||
Sign out
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</motion.li>
|
|
||||||
) : (
|
|
||||||
<motion.li
|
|
||||||
key='sign-in-link'
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
exit={{ opacity: 0 }}
|
|
||||||
>
|
|
||||||
<Link
|
|
||||||
href='/login'
|
|
||||||
className='text-sm font-medium hover:text-primary transition-colors uppercase'
|
|
||||||
>
|
|
||||||
login
|
|
||||||
</Link>
|
|
||||||
</motion.li>
|
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</ul>
|
|
||||||
|
|
||||||
{/* Mobile Hamburger Button */}
|
{/* Desktop Navigation Links */}
|
||||||
<button
|
<ul className='hidden md:flex flex-row items-center gap-6 shrink-0'>
|
||||||
onClick={() => setMobileMenuOpen(true)}
|
{navbarRoutes.map((route) => (
|
||||||
className='flex md:hidden flex-row items-center justify-center p-2 -mr-2 rounded-md hover:bg-text/5 transition-colors'
|
<Link
|
||||||
aria-label='Open menu'
|
key={route.href}
|
||||||
>
|
href={route.href}
|
||||||
<MenuIcon className='h-5 w-5' />
|
title={`Navigate to ${route.title}`}
|
||||||
</button>
|
className='text-sm font-medium hover:text-primary transition-colors uppercase'
|
||||||
</motion.nav>
|
>
|
||||||
|
{route.title}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<AnimatePresence
|
||||||
|
mode='popLayout'
|
||||||
|
initial={false}
|
||||||
|
>
|
||||||
|
{isSessionLoading ? (
|
||||||
|
<motion.li
|
||||||
|
key='loading-placeholder'
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className='w-20 h-8 rounded-lg bg-white/3 animate-pulse'
|
||||||
|
/>
|
||||||
|
) : session ? (
|
||||||
|
<motion.li
|
||||||
|
key='dashboard-link'
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className='relative'
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setUserMenuOpen(!userMenuOpen)
|
||||||
|
}
|
||||||
|
className='flex items-center gap-2 text-sm font-medium hover:text-primary transition-colors uppercase'
|
||||||
|
>
|
||||||
|
{session.user.image ? (
|
||||||
|
<Image
|
||||||
|
src={session.user.image}
|
||||||
|
alt=''
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
unoptimized
|
||||||
|
className='h-5 w-5 rounded-full'
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className='h-5 w-5 rounded-full bg-secondary flex items-center justify-center'>
|
||||||
|
<User className='h-3 w-3 text-text' />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span className='max-w-25 truncate'>
|
||||||
|
{session.user.name}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
{userMenuOpen && (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className='fixed inset-0 z-40'
|
||||||
|
onClick={() =>
|
||||||
|
setUserMenuOpen(false)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className='absolute right-0 top-full mt-1 w-48 bg-[#1a1020] border border-white/10 rounded-lg shadow-lg z-50 py-1'>
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
setUserMenuOpen(false)
|
||||||
|
await authClient.signOut()
|
||||||
|
}}
|
||||||
|
className='w-full flex items-center gap-2 px-3 py-2 text-sm text-text/70 hover:text-text hover:bg-text/5 transition-colors'
|
||||||
|
>
|
||||||
|
<LogOut className='h-4 w-4' />
|
||||||
|
Sign out
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</motion.li>
|
||||||
|
) : (
|
||||||
|
<motion.li
|
||||||
|
key='sign-in-link'
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href='/login'
|
||||||
|
className='text-sm font-medium hover:text-primary transition-colors uppercase'
|
||||||
|
>
|
||||||
|
login
|
||||||
|
</Link>
|
||||||
|
</motion.li>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{/* Mobile Hamburger Button */}
|
||||||
|
<button
|
||||||
|
onClick={() => setMobileMenuOpen(true)}
|
||||||
|
className='flex md:hidden flex-row items-center justify-center p-2 -mr-2 rounded-md hover:bg-text/5 transition-colors'
|
||||||
|
aria-label='Open menu'
|
||||||
|
>
|
||||||
|
<MenuIcon className='h-5 w-5' />
|
||||||
|
</button>
|
||||||
|
</motion.nav>
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* Mobile Sidebar Overlay */}
|
{/* Mobile Sidebar Overlay */}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
|
|||||||
Reference in New Issue
Block a user