"use client"; // Overlay wrapper for the nav-driven section panels of the /trade-in/v2 // "МЕРА Оценка" design. Faithful port of the design overlay container // (МЕРА Оценка.dc.html lines 443-453 + 615-616): a glass/blur absolute layer // with HUD corner brackets, a header (section number + title + "← К ОЦЕНКЕ" // button) and a scrollable body that swaps the active view. import { useEffect, useRef } from "react"; import { tokens } from "./tokens"; import { overlayNums, overlayTitles } from "./fixtures"; import HistoryView from "./HistoryView"; import SourcesView from "./SourcesView"; import AnalyticsView from "./AnalyticsView"; import { CacheView } from "./CacheView"; import type { CacheData, HistoryData, SourcesData } from "./mappers"; import type { Analytics } from "./types"; interface SectionOverlayProps { active: number; onClose: () => void; onNavigate: (i: number) => void; // Mapped overlay data, fed by page.tsx (mapHistory / mapSources / mapAnalytics // / mapCache). Optional so unwired/storybook usage falls back to each view's // fixture default. history?: HistoryData; sources?: SourcesData; analytics?: Analytics; cache?: CacheData; } export default function SectionOverlay({ active, onClose, onNavigate, history, sources, analytics, cache, }: SectionOverlayProps) { // Modal dialog plumbing. `dialogRef` is the dialog root, `lastFocused` keeps // the element that had focus before the overlay opened so we can restore it on // close, and `onCloseRef` holds the latest onClose so the mount-only effect // (deps []) never re-runs / re-traps focus on parent re-renders even though // page.tsx passes a fresh arrow each render. const dialogRef = useRef(null); const lastFocused = useRef(null); const onCloseRef = useRef(onClose); onCloseRef.current = onClose; useEffect(() => { const dialog = dialogRef.current; // Remember the trigger, then move focus into the dialog. lastFocused.current = document.activeElement; dialog?.focus(); function getFocusable(): HTMLElement[] { if (!dialog) return []; const nodes = dialog.querySelectorAll( 'button, [href], input, select, [tabindex]:not([tabindex="-1"])', ); return Array.from(nodes).filter((el) => !el.hasAttribute("disabled")); } function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape") { e.preventDefault(); onCloseRef.current(); return; } if (e.key !== "Tab" || !dialog) return; const focusable = getFocusable(); if (focusable.length === 0) { // Nothing tabbable inside — keep focus pinned on the dialog itself. e.preventDefault(); dialog.focus(); return; } const first = focusable[0]; const last = focusable[focusable.length - 1]; const activeEl = document.activeElement; const inside = dialog.contains(activeEl); if (e.shiftKey) { if (activeEl === first || activeEl === dialog || !inside) { e.preventDefault(); last.focus(); } } else if (activeEl === last || !inside) { e.preventDefault(); first.focus(); } } document.addEventListener("keydown", onKeyDown); return () => { document.removeEventListener("keydown", onKeyDown); // Return focus to whatever opened the overlay. (lastFocused.current as HTMLElement | null)?.focus?.(); }; // Mount-only: refs (dialogRef/onCloseRef) are stable, so no extra deps. }, []); return (
{/* HUD corner brackets */}
{/* overlay header */}
{overlayNums[active]} {overlayTitles[active]}
{/* overlay body */}
{active === 1 && } {active === 2 && } {active === 3 && ( )} {active === 4 && }
); }