Initial commit
This commit is contained in:
184
web/src/components/layout/Navigation.tsx
Executable file
184
web/src/components/layout/Navigation.tsx
Executable file
@@ -0,0 +1,184 @@
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
to: '/discover',
|
||||
label: 'Discover',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: '/likes',
|
||||
label: 'Likes',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: '/sent-pings',
|
||||
label: 'Pings',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: '/messages',
|
||||
label: 'Messages',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: '/profile',
|
||||
label: 'Profile',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
to: '/settings',
|
||||
label: 'Settings',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" className="w-6 h-6">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export function Navigation() {
|
||||
const location = useLocation();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Desktop Navigation - Side rail */}
|
||||
<nav className="hidden md:flex fixed left-0 top-0 bottom-0 w-20 flex-col items-center py-8 z-50 bg-[var(--color-void)] border-r border-white/5">
|
||||
{/* Logo */}
|
||||
<div className="mb-12">
|
||||
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-desire)] to-[var(--color-desire-glow)] flex items-center justify-center shadow-lg">
|
||||
<span className="text-white font-display font-bold text-lg">F</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Nav Items */}
|
||||
<div className="flex flex-col gap-2 flex-1">
|
||||
{navItems.map((item, index) => {
|
||||
const isActive = location.pathname === item.to ||
|
||||
(item.to === '/discover' && location.pathname === '/');
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
className={`
|
||||
relative w-12 h-12 flex items-center justify-center rounded-xl
|
||||
transition-all duration-300 ease-out group
|
||||
${isActive
|
||||
? 'text-white'
|
||||
: 'text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)]'
|
||||
}
|
||||
${mounted ? 'animate-fade-up' : 'opacity-0'}
|
||||
`}
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
{/* Active indicator */}
|
||||
{isActive && (
|
||||
<div className="absolute inset-0 rounded-xl bg-[var(--color-desire)]/10 border border-[var(--color-desire)]/30" />
|
||||
)}
|
||||
|
||||
{/* Glow effect on active */}
|
||||
{isActive && (
|
||||
<div className="absolute inset-0 rounded-xl bg-[var(--color-desire)]/5 blur-xl" />
|
||||
)}
|
||||
|
||||
{/* Icon */}
|
||||
<span className={`relative z-10 transition-transform duration-300 ${isActive ? 'scale-110' : 'group-hover:scale-110'}`}>
|
||||
{item.icon}
|
||||
</span>
|
||||
|
||||
{/* Tooltip */}
|
||||
<div className="
|
||||
absolute left-full ml-3 px-3 py-1.5 rounded-lg
|
||||
bg-[var(--color-surface-elevated)] border border-[var(--glass-border)]
|
||||
text-sm font-medium text-white
|
||||
opacity-0 pointer-events-none translate-x-1
|
||||
group-hover:opacity-100 group-hover:translate-x-0
|
||||
transition-all duration-200
|
||||
whitespace-nowrap
|
||||
">
|
||||
{item.label}
|
||||
</div>
|
||||
|
||||
{/* Active dot */}
|
||||
{isActive && (
|
||||
<div className="absolute -right-1 w-1.5 h-1.5 rounded-full bg-[var(--color-desire)]" />
|
||||
)}
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Mobile Navigation - Bottom bar */}
|
||||
<nav className="
|
||||
md:hidden fixed bottom-0 left-0 right-0 z-50
|
||||
glass rounded-t-2xl
|
||||
safe-area-inset-bottom
|
||||
">
|
||||
<div className="flex justify-around py-2 px-2">
|
||||
{navItems.map((item, index) => {
|
||||
const isActive = location.pathname === item.to ||
|
||||
(item.to === '/discover' && location.pathname === '/');
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
key={item.to}
|
||||
to={item.to}
|
||||
className={`
|
||||
relative flex flex-col items-center gap-1 px-4 py-2 rounded-xl
|
||||
transition-all duration-300
|
||||
${isActive
|
||||
? 'text-[var(--color-desire)]'
|
||||
: 'text-[var(--color-text-muted)]'
|
||||
}
|
||||
${mounted ? 'animate-fade-up' : 'opacity-0'}
|
||||
`}
|
||||
style={{ animationDelay: `${index * 50}ms` }}
|
||||
>
|
||||
{/* Active background */}
|
||||
{isActive && (
|
||||
<div className="absolute inset-0 rounded-xl bg-[var(--color-desire)]/10" />
|
||||
)}
|
||||
|
||||
<span className={`relative z-10 transition-transform duration-200 ${isActive ? 'scale-110' : ''}`}>
|
||||
{item.icon}
|
||||
</span>
|
||||
<span className={`relative z-10 text-[10px] font-medium ${isActive ? 'text-[var(--color-desire)]' : ''}`}>
|
||||
{item.label}
|
||||
</span>
|
||||
</NavLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user