Files
portfolio/src/widgets/Navigation/ui/SidebarNav.tsx
T

103 lines
3.0 KiB
TypeScript
Raw Normal View History

'use client';
import { useState, useEffect } from 'react';
import { cn } from '$shared/lib';
import type { NavItem } from '../model/types';
interface Props {
/**
* Navigation items to render
*/
items: NavItem[];
}
/**
* Fixed sidebar navigation, visible on lg+ screens.
*/
export function SidebarNav({ items }: Props) {
const [activeSection, setActiveSection] = useState('bio');
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(entry.target.id);
}
});
},
{ rootMargin: '-20% 0px -70% 0px', threshold: 0 },
);
items.forEach((item) => {
const el = document.getElementById(item.id);
if (el) observer.observe(el);
});
return () => observer.disconnect();
}, [items]);
/**
* Scrolls to the section by id with a 40px offset.
*/
function scrollToSection(id: string) {
const el = document.getElementById(id);
if (el) {
const top = el.getBoundingClientRect().top + window.scrollY - 40;
window.scrollTo({ top, behavior: 'smooth' });
}
}
return (
<nav className="fixed top-0 left-0 h-screen w-full lg:w-1/3 bg-ochre-clay brutal-border-right hidden lg:block overflow-y-auto z-50">
<div className="px-8 py-12 space-y-2">
<div className="mb-12">
<h2>Index</h2>
<div className="brutal-border-top pt-4">
<p className="text-sm opacity-60">Digital Monograph</p>
</div>
</div>
{items.map((item) => {
const isActive = activeSection === item.id;
return (
<button
key={item.id}
onClick={() => scrollToSection(item.id)}
className={cn(
'w-full text-left brutal-border bg-ochre-clay px-6 py-4 transition-all duration-300',
isActive
? 'shadow-[12px_12px_0_var(--carbon-black)] opacity-100 translate-x-0'
: 'opacity-40 shadow-none hover:opacity-60',
)}
>
<div className="flex items-baseline gap-4">
<span className="text-sm opacity-60">{item.number}</span>
<span className="font-heading text-xl font-black">{item.label}</span>
</div>
</button>
);
})}
<div className="mt-12 pt-12 brutal-border-top">
<p className="text-sm uppercase tracking-wider mb-4 opacity-60">Quick Links</p>
<div className="space-y-3">
<a href="mailto:hello@allmy.work" className="block">
Email
</a>
<a href="https://linkedin.com" className="block">
LinkedIn
</a>
<a href="https://instagram.com" className="block">
Instagram
</a>
<a href="https://are.na" className="block">
Are.na
</a>
</div>
</div>
</div>
</nav>
);
}