"use client"; import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from "react"; interface SidebarContextValue { collapsed: boolean; toggleCollapsed: () => void; mobileOpen: boolean; setMobileOpen: (open: boolean) => void; isMobile: boolean; } const SidebarContext = createContext(undefined); /** Breakpoint below which we treat the viewport as "mobile" (matches CSS max-width: 767px). */ const MOBILE_MAX_WIDTH = 767; export function SidebarProvider({ children }: { children: ReactNode }): React.JSX.Element { const [collapsed, setCollapsed] = useState(false); const [mobileOpen, setMobileOpen] = useState(false); const [isMobile, setIsMobile] = useState(false); // Initialise and track mobile breakpoint using matchMedia useEffect((): (() => void) => { const mql = window.matchMedia(`(max-width: ${String(MOBILE_MAX_WIDTH)}px)`); const handleChange = (e: MediaQueryListEvent): void => { setIsMobile(e.matches); // Close mobile sidebar when viewport grows out of mobile range if (!e.matches) { setMobileOpen(false); } }; // Set initial value synchronously setIsMobile(mql.matches); mql.addEventListener("change", handleChange); return (): void => { mql.removeEventListener("change", handleChange); }; }, []); const toggleCollapsed = useCallback((): void => { setCollapsed((prev) => !prev); }, []); const value: SidebarContextValue = { collapsed, toggleCollapsed, mobileOpen, setMobileOpen, isMobile, }; return {children}; } export function useSidebar(): SidebarContextValue { const context = useContext(SidebarContext); if (context === undefined) { throw new Error("useSidebar must be used within SidebarProvider"); } return context; }