import { useEffect, useRef, type ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import type { PageId } from '../../lib/urlState'; import { useBackdropClose } from '../../lib/useBackdropClose'; export interface NavItem { id: PageId; /** i18n key resolved against the `layout` namespace at render time. */ labelKey: string; } interface NavDrawerProps { open: boolean; onClose: () => void; visibleNav: NavItem[]; currentPage: PageId; onNavigate: (page: PageId) => void; appName: string; logoUrl: string | null; returnFocusRef?: React.RefObject; } const ICON_PROPS = { width: 22, height: 22, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.7, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const, 'aria-hidden': true, }; const NAV_ICONS: Record = { tasks: ( ), schedules: ( ), pieces: ( ), captcha: ( ), settings: ( ), users: ( ), help: ( ), userfolder: ( ), usage: ( ), }; export function NavDrawer({ open, onClose, visibleNav, currentPage, onNavigate, appName, logoUrl, returnFocusRef, }: NavDrawerProps) { const { t } = useTranslation('layout'); const panelRef = useRef(null); const firstItemRef = useRef(null); const backdrop = useBackdropClose(onClose); useEffect(() => { if (!open) return; const timeout = window.setTimeout(() => { (firstItemRef.current ?? panelRef.current)?.focus(); }, 0); return () => { window.clearTimeout(timeout); returnFocusRef?.current?.focus(); }; }, [open, returnFocusRef]); useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') { e.stopPropagation(); onClose(); } }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [open, onClose]); useEffect(() => { if (!open) return; const prev = document.documentElement.style.overflow; document.documentElement.style.overflow = 'hidden'; return () => { document.documentElement.style.overflow = prev; }; }, [open]); const onPanelKeyDown = (e: React.KeyboardEvent) => { if (e.key !== 'Tab' || !panelRef.current) return; const focusable = panelRef.current.querySelectorAll( 'button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])', ); if (focusable.length === 0) return; const first = focusable[0]; const last = focusable[focusable.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }; return ( <>
); }