"use client" import React, { useRef } from 'react' import { usePreventScroll } from '@react-aria/overlays' import { cn } from '../../utils' import { MobileNavConfig, NavigationItem } from '../../types/navigation' import { Button } from '../ui/button' import { OVERLAY_BACKDROP_CLASS } from '../ui/drawer' import { useFocusTrap } from '../../hooks/ui/use-focus-trap' import { useHeaderHeight } from '../../hooks/ui/use-header-height' import { X } from 'lucide-react' /** DOM id of the panel — referenced by the header hamburger's `aria-controls`. */ export const MOBILE_NAV_PANEL_ID = 'mobile-nav-panel' export interface MobileNavPanelProps { isOpen: boolean config: MobileNavConfig } export function MobileNavPanel({ isOpen, config }: MobileNavPanelProps) { const panelRef = useRef(null) // Shared ref-counted, iOS-aware scroll lock (react-aria) — one counter with // the modals and chat overlays, restores prior styles on release. usePreventScroll({ isDisabled: !isOpen }) // Initial focus, Tab containment, Escape-to-close, guarded focus restore. useFocusTrap(panelRef, isOpen, { onEscape: config.onClose }) // The panel is layout-mounted on every page — observers only while open. const headerHeight = useHeaderHeight(64, { enabled: isOpen }) if (!isOpen) return null const renderNavigationItem = (item: NavigationItem) => { if (item.element) { return
{item.element}
} const handleClick = () => { if (item.onClick) { item.onClick() } config.onClose?.() } if (item.href) { return ( ) } return ( ) } return ( <> {/* Backdrop - closes nav when clicked outside */}
{/* Navigation Panel - top-anchored floating card */} ) }