haixunMaster/haixun-backend/web/src/components/AppSidebar.tsx

80 lines
2.5 KiB
TypeScript
Raw Normal View History

2026-06-23 16:55:10 +00:00
import { NavLink, useLocation } from 'react-router-dom'
2026-06-24 06:04:54 +00:00
import { navGroupsForOnboarding, onboardingGlowClass, shouldGlowNav } from '../lib/onboarding'
2026-06-23 16:55:10 +00:00
import type { AcAppKey } from '../lib/acAssets'
import { useOnboarding } from '../onboarding/OnboardingContext'
import { AcIcon } from './AcIcon'
function SidebarNavItem({
to,
label,
icon,
end,
matchPrefix,
2026-06-24 06:04:54 +00:00
guideGlow,
2026-06-23 16:55:10 +00:00
}: {
to: string
label: string
icon: AcAppKey
end?: boolean
matchPrefix?: string
2026-06-24 06:04:54 +00:00
guideGlow?: boolean
2026-06-23 16:55:10 +00:00
}) {
const { pathname } = useLocation()
return (
<NavLink
to={to}
end={end}
className={({ isActive }) => {
const prefixActive = matchPrefix ? pathname.startsWith(matchPrefix) : false
const active = isActive || prefixActive
2026-06-24 06:04:54 +00:00
return `ac-sidebar-nav-item relative ${active ? 'ac-sidebar-nav-item--active' : ''} ${onboardingGlowClass(!!guideGlow)}`
2026-06-23 16:55:10 +00:00
}}
>
2026-06-24 06:04:54 +00:00
{guideGlow ? (
<span className="hx-guide-badge" aria-hidden>
</span>
) : null}
2026-06-23 16:55:10 +00:00
<AcIcon app={icon} size="sm" className="ac-sidebar-nav-icon shrink-0" />
<span className="min-w-0 truncate">{label}</span>
</NavLink>
)
}
export function AppSidebar() {
2026-06-24 06:04:54 +00:00
const { pathname } = useLocation()
const { isComplete, nextStep } = useOnboarding()
2026-06-23 16:55:10 +00:00
const groups = navGroupsForOnboarding(isComplete)
return (
<aside className="ac-sidebar hidden lg:flex" aria-label="側欄導覽">
{!isComplete ? (
<p className="ac-sidebar-onboarding-hint px-4 pb-2 pt-3 text-xs leading-relaxed text-ink-secondary">
</p>
) : null}
<nav className="ac-sidebar-nav ac-sidebar-nav--top">
{groups.map((group, index) => (
<div key={group.label} className="ac-sidebar-nav-group">
{index > 0 ? <div className="ac-sidebar-nav-divider" aria-hidden /> : null}
<p className="ac-sidebar-nav-label">{group.label}</p>
<ul className="space-y-0.5">
{group.items.map((item) => (
<li key={item.to}>
<SidebarNavItem
to={item.to}
label={item.label}
icon={item.icon}
end={item.end}
matchPrefix={item.matchPrefix}
2026-06-24 06:04:54 +00:00
guideGlow={shouldGlowNav(nextStep, item.to, pathname)}
2026-06-23 16:55:10 +00:00
/>
</li>
))}
</ul>
</div>
))}
</nav>
</aside>
)
}