/* Hallmark · component: mobile side navigation · genre: modern-minimal · theme: Quiet · pre-emit critique: P5 H5 E5 S5 R5 V5 */ import { Link } from '@tanstack/react-router' import { Calendar, ChevronRight, Command, Mail, Moon, Plus, Sun, Users } from 'lucide-react' import { type ReactNode, useEffect, useRef } from 'react' import { Tooltip, TooltipContent, TooltipTrigger } from '#shared/components/ui/tooltip' import { initials } from '#shared/lib/presentation' import { cn } from '#shared/lib/utils' import { APP_RAIL_WIDTH_CLASS, CHROME_ROW_CLASS } from '../config/layout.js' import { CALENDAR_HOME_PATH, CONTACTS_HOME_PATH, MAIL_HOME_PATH, SETTINGS_PATH, } from '../config/route-paths.js' import { themeToggleLabel, toggleTheme } from '../config/theme.js' import { useThemeToggleState } from '../lib/use-theme-toggle-state.js' import { useUserPreferences } from '../preferences/user-preferences.js' export type MailboxAccountOption = { email: string handle: string active: boolean } type AppRailNavProps = { email: string displayName?: string accounts?: MailboxAccountOption[] active: 'mail' | 'calendar' | 'contacts' | 'settings' onOpenCommandPalette?: () => void } type AppRailMobileNavProps = AppRailNavProps & { onNavigate: () => void showDestinations?: boolean } function formatOrgLabel(appName: string): string { return appName .split(/[-_\s]+/) .filter(Boolean) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(' ') } export function AppRailLogo({ appName, className }: { appName: string; className?: string }) { const orgLabel = formatOrgLabel(appName) const orgInitials = initials(appName) return (
) } export function AppRailNav({ email, displayName, accounts = [], active, onOpenCommandPalette, }: AppRailNavProps) { const { isDark, mounted } = useThemeToggleState() const [preferences] = useUserPreferences() const effectiveDisplayName = displayName || preferences.displayName const accountLabel = effectiveDisplayName ? `${effectiveDisplayName} · ${email}` : email return ( ) } /** * The mobile counterpart to the persistent desktop rail. It is intended for a * temporary navigation sheet, so it uses labelled, touch-sized rows instead of * reserving a narrow column in the app viewport. */ export function AppRailMobileNav({ email, displayName, accounts = [], active, onOpenCommandPalette, onNavigate, showDestinations = true, }: AppRailMobileNavProps) { const { isDark, mounted } = useThemeToggleState() const [preferences] = useUserPreferences() const effectiveDisplayName = displayName || preferences.displayName const accountLabel = effectiveDisplayName ? `${effectiveDisplayName} · ${email}` : email const accountName = effectiveDisplayName || email return ( ) } function DesktopAccountSwitcher({ accounts }: { accounts: MailboxAccountOption[] }) { const active = accounts.find((account) => account.active) const detailsRef = useRefSwitch inbox
{accounts.map((account) => ( ))}