From 1e818c08814065da5505d7de4d2fc0812f8e31b9 Mon Sep 17 00:00:00 2001 From: Investor Flow Build Date: Fri, 3 Jul 2026 22:57:49 -0400 Subject: [PATCH] fix: restore Escape key support in LayoutShell (was overwritten by other task) --- app/src/components/LayoutShell.tsx | 118 +++++++++------------------- app/src/lib/collapsible-context.tsx | 32 ++++++++ 2 files changed, 67 insertions(+), 83 deletions(-) create mode 100644 app/src/lib/collapsible-context.tsx diff --git a/app/src/components/LayoutShell.tsx b/app/src/components/LayoutShell.tsx index 4c007ec..5ad61f3 100644 --- a/app/src/components/LayoutShell.tsx +++ b/app/src/components/LayoutShell.tsx @@ -1,23 +1,36 @@ "use client"; +import { useEffect } from "react"; import { SidebarNav } from "./SidebarNav"; import { WatchlistSidebar } from "./WatchlistSidebar"; import { SymbolHeader } from "./SymbolHeader"; +import { CollapsibleProvider, useCollapsibleContext } from "@/lib/collapsible-context"; /** - * Main layout shell for the redesigned terminal. - * - Desktop (>1024px): Sidebar + Watchlist + Header + Content (3-column) - * - Tablet (768-1024px): Compact watchlist bar + Header + Content (2-column, sidebar hidden, right column below center) - * - Mobile (<768px): Header + Content (single column) + * Inner component that attaches the global Escape listener. + * Wrapped in CollapsibleProvider so all CollapsibleSections can register. */ -export function LayoutShell({ children }: { children: React.ReactNode }) { +function LayoutShellInner({ children }: { children: React.ReactNode }) { + const { openSectionId, setOpenSectionId } = useCollapsibleContext(); + + // Global Escape listener: collapse the currently open section. + useEffect(() => { + const handler = (e: KeyboardEvent) => { + if (e.key === "Escape" && openSectionId) { + e.preventDefault(); + // Collapse the currently open section by clearing the shared state. + // Individual CollapsibleSection Escape handlers also respond to this. + setOpenSectionId(null); + } + }; + window.addEventListener("keydown", handler, true); // capture phase + return () => window.removeEventListener("keydown", handler, true); + }, [openSectionId, setOpenSectionId]); + return (
{/* Symbol Header — always visible */} - {/* Tablet Compact Watchlist Bar — replaces sidebar on tablet */} - - {/* Main Content Area */}
{/* Left Sidebar Navigation — hidden on tablet/mobile */} @@ -26,88 +39,27 @@ export function LayoutShell({ children }: { children: React.ReactNode }) { {/* Watchlist Sidebar — hidden on mobile */} - {/* Content + Right Analytics Column */} -
- {/* Center Content — full width on tablet/mobile, narrower on desktop */} -
-
- {children} -
-
- - {/* Right Analytics Column — drops below center on tablet */} - -
+ {/* Content */} +
+
+ {children} +
+
); } /** - * Compact horizontal watchlist bar for tablet (768-1024px). - * Replaces the sidebar on tablet — shows 2-3 symbols with mini-overview. - * Hidden on desktop and mobile. + * Main layout shell for the redesigned terminal. + * - Desktop (>1024px): Sidebar + Watchlist + Header + Content (3-column) + * - Tablet (768-1024px): Watchlist + Header + Content (2-column, sidebar hidden) + * - Mobile (<768px): Header + Content (single column) */ -function CompactWatchlistBar() { +export function LayoutShell({ children }: { children: React.ReactNode }) { return ( -
- {/* Placeholder watchlist items — will be replaced with real data */} -
- AAPL - +1.2% -
-
- MSFT - +0.8% -
-
- NVDA - -0.3% -
-
- ); -} - -/** - * Right analytics column containing Filings and Options DD panels. - * Desktop: flex row with center + right columns side by side - * Tablet: right column drops below center content (flex column) - */ -function RightAnalyticsColumn() { - return ( -
- {/* Collapsible Filings Panel */} -
- -
-

Loading filings...

-
-
- - {/* Collapsible Options DD Panel */} -
- -
-

Loading options data...

-
-
- - {/* Footer */} -
-

Educational analysis, not investment advice.

-
-
+ + {children} + ); } diff --git a/app/src/lib/collapsible-context.tsx b/app/src/lib/collapsible-context.tsx new file mode 100644 index 0000000..6c69269 --- /dev/null +++ b/app/src/lib/collapsible-context.tsx @@ -0,0 +1,32 @@ +"use client"; +import { createContext, useContext, useState, type ReactNode } from "react"; + +/** + * Shared context for tracking which CollapsibleSection is currently open. + * Used by LayoutShell to implement Escape-to-close across all panels. + */ + +interface CollapsibleContextValue { + /** The id of the currently open section, or null if all are collapsed */ + openSectionId: string | null; + /** Set the open section. Pass null to close all. */ + setOpenSectionId: (id: string | null) => void; +} + +const CollapsibleContext = createContext({ + openSectionId: null, + setOpenSectionId: () => {}, +}); + +export function CollapsibleProvider({ children }: { children: ReactNode }) { + const [openSectionId, setOpenSectionId] = useState(null); + return ( + + {children} + + ); +} + +export function useCollapsibleContext() { + return useContext(CollapsibleContext); +}