/** * AppNav — top navigation bar (phone) / left sidebar (tablet/desktop). * * UI-SPEC §AppNav: * - Phone: 48px top bar, app name "FamilySync" left, user color swatch right * - Tablet/Desktop: 240px left sidebar, app name + ColorLegend * - Accent colors NOT on chrome (60/30/10 split) * * Reviewer note (UI-SPEC §Post-Verification): * - Phone-nav avatar/color swatch needs aria-label + title (icon-only affordance) * - Grid is primary focal point; AppNav is secondary chrome */ import { NavLink } from 'react-router'; import { CalendarDays, List, ShieldCheck } from 'lucide-react'; import { ColorLegend, type LegendMember } from './ColorLegend.js'; interface AppNavProps { members?: LegendMember[]; currentUserColor?: string; currentUserName?: string; /** Called when the user avatar is tapped — opens the Settings sheet. */ onOpenSettings?: () => void; /** When true, renders the Admin nav entry (ShieldCheck). UX gating only (D-03). */ isAdmin?: boolean; } export function AppNav({ members = [], currentUserColor, currentUserName, onOpenSettings, isAdmin = false, }: AppNavProps) { const isMobile = typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches; if (isMobile) { return ( ); } return ( ); } /** Phone: 48px top bar — app name left, user avatar right */ function PhoneNav({ currentUserColor, currentUserName, onOpenSettings, }: { currentUserColor?: string; currentUserName?: string; onOpenSettings?: () => void; }) { const displayName = currentUserName ?? 'User'; const color = currentUserColor ?? 'var(--color-member-0)'; return (
{/* App name */} FamilySync {/* User avatar — promoted to button to open Settings sheet (D-09) */}
); } /** Tablet/Desktop: 240px left sidebar — app name + nav links + color legend + avatar */ function DesktopNav({ members, currentUserColor, currentUserName, onOpenSettings, isAdmin = false, }: { members: LegendMember[]; currentUserColor?: string; currentUserName?: string; onOpenSettings?: () => void; isAdmin?: boolean; }) { const navLinkStyle = ({ isActive }: { isActive: boolean }): React.CSSProperties => ({ display: 'flex', alignItems: 'center', gap: '10px', padding: 'var(--space-2) var(--space-3, 12px)', borderRadius: 'var(--space-1, 4px)', textDecoration: 'none', fontSize: 'var(--text-body-size, 15px)', fontWeight: isActive ? 600 : 400, color: isActive ? 'var(--color-member-0)' : 'var(--color-text-primary)', background: isActive ? 'color-mix(in srgb, var(--color-member-0) 10%, transparent)' : 'transparent', minHeight: '44px', transition: 'color 0.1s ease, background 0.1s ease', }); return ( ); }