Import source: cshare-app/src/components/Navbar
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,165 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
|
import { MenuIcon, XIcon } from './Icons';
|
||||||
|
|
||||||
|
export default function Navbar() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const [mobileOpen, setMobileOpen] = useState(false);
|
||||||
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onScroll = () => setScrolled(window.scrollY > 20);
|
||||||
|
window.addEventListener('scroll', onScroll, { passive: true });
|
||||||
|
return () => window.removeEventListener('scroll', onScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{ path: '/', label: 'Home' },
|
||||||
|
{ path: '/upload', label: 'Upload' },
|
||||||
|
{ path: '/dashboard', label: 'Dashboard' },
|
||||||
|
{ path: '/preview', label: 'Preview' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const extraLinks = [
|
||||||
|
{
|
||||||
|
label: 'Features',
|
||||||
|
action: () => {
|
||||||
|
navigate('/');
|
||||||
|
setTimeout(() => document.getElementById('features')?.scrollIntoView({ behavior: 'smooth' }), 150);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'How it Works',
|
||||||
|
action: () => {
|
||||||
|
navigate('/');
|
||||||
|
setTimeout(() => document.getElementById('how-it-works')?.scrollIntoView({ behavior: 'smooth' }), 150);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ label: 'Pricing', action: () => go('/pricing') },
|
||||||
|
{ label: 'Docs', action: () => go('/docs') },
|
||||||
|
];
|
||||||
|
|
||||||
|
const go = (path) => {
|
||||||
|
navigate(path);
|
||||||
|
setMobileOpen(false);
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav
|
||||||
|
className="navbar"
|
||||||
|
style={{
|
||||||
|
boxShadow: scrolled ? '0 1px 20px rgba(0,0,0,0.3)' : 'none',
|
||||||
|
borderBottomColor: scrolled ? 'var(--border)' : 'var(--border-subtle)',
|
||||||
|
transition: 'all var(--transition-base)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="navbar-logo" onClick={() => go('/')}>
|
||||||
|
<div
|
||||||
|
className="logo-mark"
|
||||||
|
style={{ transition: 'all var(--transition-base)' }}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.boxShadow = '0 0 16px rgba(74, 144, 255, 0.4)';
|
||||||
|
e.currentTarget.style.transform = 'scale(1.05)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.boxShadow = 'none';
|
||||||
|
e.currentTarget.style.transform = 'scale(1)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
C
|
||||||
|
</div>
|
||||||
|
CShare
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Desktop nav */}
|
||||||
|
<div className="navbar-links">
|
||||||
|
{links.map((link) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
key={link.path}
|
||||||
|
className={`navbar-link ${pathname === link.path ? 'active' : ''}`}
|
||||||
|
onClick={() => go(link.path)}
|
||||||
|
style={{ position: 'relative' }}
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
{pathname === link.path && (
|
||||||
|
<span style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: '-2px',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: '16px',
|
||||||
|
height: '2px',
|
||||||
|
borderRadius: '1px',
|
||||||
|
background: 'var(--accent)',
|
||||||
|
boxShadow: '0 0 6px rgba(74, 144, 255, 0.5)',
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
{extraLinks.map((link, i) => (
|
||||||
|
<button type="button" key={i} className="navbar-link" onClick={link.action}>
|
||||||
|
{link.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary btn-sm"
|
||||||
|
style={{ marginLeft: '12px' }}
|
||||||
|
onClick={() => go('/upload')}
|
||||||
|
>
|
||||||
|
Share Files
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile hamburger */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-ghost btn-icon mobile-menu-btn"
|
||||||
|
aria-label={mobileOpen ? 'Close navigation menu' : 'Open navigation menu'}
|
||||||
|
aria-controls="mobile-navigation"
|
||||||
|
aria-expanded={mobileOpen}
|
||||||
|
onClick={() => setMobileOpen((open) => !open)}
|
||||||
|
>
|
||||||
|
{mobileOpen ? <XIcon size={20} /> : <MenuIcon size={20} />}
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
{mobileOpen && (
|
||||||
|
<div id="mobile-navigation" className="mobile-menu-panel">
|
||||||
|
<div className="mobile-menu-list">
|
||||||
|
{links.map((link) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
key={link.path}
|
||||||
|
className={`mobile-menu-link ${pathname === link.path ? 'active' : ''}`}
|
||||||
|
onClick={() => go(link.path)}
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
{extraLinks.map((link, i) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
key={i}
|
||||||
|
className="mobile-menu-link"
|
||||||
|
onClick={() => { link.action(); setMobileOpen(false); }}
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-primary btn-lg mobile-menu-cta"
|
||||||
|
onClick={() => go('/upload')}
|
||||||
|
>
|
||||||
|
Share Files
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user