Files
dashboards/src/components/Header.tsx
T

147 lines
4.7 KiB
TypeScript

import { useEffect, useId, useRef, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { ChevronDown, LogOut } from 'lucide-react'
import { clearSession, getSession } from '../lib/auth'
import { logout } from '../lib/authApi'
import { navSections } from '../lib/nav'
import logo from '../images/Logo-balout-256.png'
import styles from './Header.module.css'
type HeaderProps = {
showLogout?: boolean
}
export function Header({ showLogout = true }: HeaderProps) {
const navigate = useNavigate()
const session = getSession()
const [openId, setOpenId] = useState<string | null>(null)
const navRef = useRef<HTMLElement>(null)
const navLabelId = useId()
useEffect(() => {
function handlePointerDown(event: MouseEvent) {
if (!navRef.current?.contains(event.target as Node)) {
setOpenId(null)
}
}
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Escape') setOpenId(null)
}
document.addEventListener('mousedown', handlePointerDown)
document.addEventListener('keydown', handleKeyDown)
return () => {
document.removeEventListener('mousedown', handlePointerDown)
document.removeEventListener('keydown', handleKeyDown)
}
}, [])
async function handleLogout() {
try {
await logout()
} catch {
clearSession()
}
navigate('/login')
}
function toggleSection(id: string) {
setOpenId((current) => (current === id ? null : id))
}
return (
<header className={styles.header}>
<div className={styles.inner}>
<Link to="/" className={styles.brand} onClick={() => setOpenId(null)}>
<img src={logo} alt="بلوط" className={styles.logo} />
</Link>
<nav
ref={navRef}
className={styles.nav}
aria-labelledby={navLabelId}
>
<span id={navLabelId} className={styles.srOnly}>
منوی اصلی
</span>
<ul className={styles.navList}>
{navSections.map((section) => {
const Icon = section.icon
const isOpen = openId === section.id
const hasDropdown = section.items.length > 0
return (
<li key={section.id} className={styles.navItem}>
{hasDropdown ? (
<>
<button
type="button"
className={`${styles.navTrigger} ${isOpen ? styles.navTriggerOpen : ''}`}
aria-expanded={isOpen}
aria-haspopup="menu"
onClick={() => toggleSection(section.id)}
>
<Icon size={18} strokeWidth={1.75} />
<span>{section.title}</span>
<ChevronDown
size={16}
strokeWidth={1.75}
className={`${styles.chevron} ${isOpen ? styles.chevronOpen : ''}`}
/>
</button>
{isOpen && (
<ul className={styles.dropdown} role="menu">
{section.items.map((item) => (
<li key={item.href} role="none">
<Link
to={item.href}
role="menuitem"
className={styles.dropdownLink}
onClick={() => setOpenId(null)}
>
{item.label}
</Link>
</li>
))}
</ul>
)}
</>
) : (
<Link
to={section.href}
className={styles.navTrigger}
onClick={() => setOpenId(null)}
>
<Icon size={18} strokeWidth={1.75} />
<span>{section.title}</span>
</Link>
)}
</li>
)
})}
</ul>
</nav>
{showLogout && (
<div className={styles.actions}>
{session && (
<span className={styles.userName}>{session.user.name}</span>
)}
<button
type="button"
className={styles.logoutBtn}
onClick={handleLogout}
aria-label="خروج"
>
<LogOut size={18} strokeWidth={1.75} />
<span>خروج</span>
</button>
</div>
)}
</div>
</header>
)
}