import { useEffect, useRef, useState, type ReactNode } from "react"; import { Button, Drawer, Layout } from "tdesign-react"; import { X } from "lucide-react"; import { DEFAULT_ROUTE_ID, type RouteId } from "../router"; import AppContent from "./Content"; import AppHeader from "./Header"; import AppMenu from "./Menu"; import { useDrawerFocus } from "../hooks/useDrawerFocus"; export interface AppLayoutProps { activeId?: RouteId; adminName?: string; adminUsername?: string; children: ReactNode; onNavigate: (id: RouteId) => void; onLogout?: () => void; onOpenPassword?: () => void; } export default function AppLayout({ activeId = DEFAULT_ROUTE_ID, adminName, adminUsername, children, onNavigate, onLogout, onOpenPassword }: AppLayoutProps) { const [desktopVisible, setDesktopVisible] = useState(true); const [isMobile, setIsMobile] = useState(false); const [mobileVisible, setMobileVisible] = useState(false); const menuButtonRef = useRef(null); useDrawerFocus(mobileVisible, ".tn-mobile-drawer"); useEffect(() => { const media = typeof window.matchMedia === "function" ? window.matchMedia("(max-width: 900px)") : null; const sync = () => { const mobile = media?.matches ?? window.innerWidth <= 900; setIsMobile(mobile); if (!mobile) setMobileVisible(false); }; sync(); if (!media) return; if (typeof media.addEventListener === "function") { media.addEventListener("change", sync); return () => media.removeEventListener("change", sync); } media.addListener?.(sync); return () => media.removeListener?.(sync); }, []); const restoreMenuFocus = () => { window.requestAnimationFrame(() => menuButtonRef.current?.focus()); }; const closeMobileMenu = () => { setMobileVisible(false); restoreMenuFocus(); }; const choose = (id: RouteId) => { onNavigate(id); closeMobileMenu(); }; const toggleMenu = () => { if (isMobile) { setMobileVisible(value => !value); return; } setMobileVisible(false); setDesktopVisible(value => !value); }; return ( {children} 主导航