'use client'; import { useState, useSyncExternalStore } from 'react'; import { CloseIcon } from './icons/CloseIcon'; export type SidebarPanel = { id: string; label: string; icon: React.ReactNode; component: React.ReactNode; }; function useIsWide(): boolean { return useSyncExternalStore( (cb) => { window.addEventListener('resize', cb); return () => window.removeEventListener('resize', cb); }, () => window.innerWidth >= 768, () => false, ); } /* ----------------------------- Shared sidebar logic ------------------------------*/ function useSidebarState(panels: SidebarPanel[], isWide: boolean) { const [activeId, setActiveId] = useState(null); // open first panel by default ONLY on first mount (desktop only) const [initialized, setInitialized] = useState(false); if (!initialized && isWide) { setActiveId(panels[0]?.id ?? null); setInitialized(true); } const setClosed = () => setActiveId(null); const toggle = (id: string) => { setActiveId((prev) => (prev === id ? null : id)); }; return { activeId, setActiveId, setClosed, toggle, }; } /* ----------------------------- Desktop UI ------------------------------*/ function DesktopSidebar({ activePanel, isOpen, setClosed, }: { activePanel?: SidebarPanel; isOpen: boolean; setClosed: () => void; }) { return (
{activePanel && (
{activePanel.label}
{activePanel.component}
)}
); } /* ----------------------------- Mobile UI (drawer) ------------------------------*/ function MobileSidebar({ activePanel, setClosed, }: { activePanel?: SidebarPanel; setClosed: () => void; }) { if (!activePanel) return null; return ( <> {/* Backdrop */}
{/* Drawer */}
{activePanel.label}
{activePanel.component}
); } /* ----------------------------- Icon rail (shared) ------------------------------*/ function IconRail({ panels, effectiveId, toggle, }: { panels: SidebarPanel[]; effectiveId: string | null; toggle: (id: string, effectiveId: string | null) => void; }) { return (
{panels.map((panel) => ( ))}
); } /* ----------------------------- Main Sidebar (controller) ------------------------------*/ export default function Sidebar({ panels }: { panels: SidebarPanel[] }) { const isWide = useIsWide(); const { activeId, setClosed, toggle } = useSidebarState(panels, isWide); const effectiveId = activeId; const activePanel = panels.find((p) => p.id === effectiveId); const isOpen = !!activePanel; return (
{/* Desktop */} {isWide && ( )} {/* Mobile */} {!isWide && ( )} {/* Icon rail */}
); }