"use client"; import { useEffect, useRef, type ReactNode, type RefObject } from "react"; import { X } from "lucide-react"; import { Button } from "@/components/ui/Button"; import { handleTabTrap } from "@/lib/a11y/focusTrap"; import { useRestoreFocus } from "@/lib/a11y/useRestoreFocus"; import { useBodyScrollLock } from "@/lib/ui/useBodyScrollLock"; import { overlayScrim } from "@/lib/ui/variants"; interface DrawerProps { open: boolean; onClose: () => void; // 滑出方向:导航在左、本章助手在右。 side?: "left" | "right"; // 无障碍标签(role=dialog)。 label: string; // 抽屉面板 id:供触发按钮 aria-controls 指向(无障碍关联)。 id?: string; triggerRef?: RefObject; children: ReactNode; } // 移动端侧滑抽屉((null); const closeRef = useRef(null); useBodyScrollLock(open); useRestoreFocus(open, triggerRef); useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent): void => { if (e.key === "Escape") { e.preventDefault(); onClose(); } }; window.addEventListener("keydown", onKey); // 焦点陷阱首焦点落在具名关闭按钮上(而非整个面板)。 closeRef.current?.focus(); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); if (!open) return null; const sideClass = side === "left" ? "left-0 border-r" : "right-0 border-l"; return (
); }