All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
68 lines
1.6 KiB
TypeScript
68 lines
1.6 KiB
TypeScript
'use client';
|
|
|
|
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
|
|
|
|
interface SidebarContextValue {
|
|
collapsed: boolean;
|
|
toggleCollapsed: () => void;
|
|
mobileOpen: boolean;
|
|
setMobileOpen: (open: boolean) => void;
|
|
isMobile: boolean;
|
|
}
|
|
|
|
const MOBILE_MAX_WIDTH = 767;
|
|
const SidebarContext = createContext<SidebarContextValue | undefined>(undefined);
|
|
|
|
export function SidebarProvider({ children }: { children: ReactNode }): React.JSX.Element {
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
const [isMobile, setIsMobile] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia(`(max-width: ${String(MOBILE_MAX_WIDTH)}px)`);
|
|
|
|
const syncState = (matches: boolean): void => {
|
|
setIsMobile(matches);
|
|
if (!matches) {
|
|
setMobileOpen(false);
|
|
}
|
|
};
|
|
|
|
syncState(mediaQuery.matches);
|
|
|
|
const handleChange = (event: MediaQueryListEvent): void => {
|
|
syncState(event.matches);
|
|
};
|
|
|
|
mediaQuery.addEventListener('change', handleChange);
|
|
|
|
return () => {
|
|
mediaQuery.removeEventListener('change', handleChange);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<SidebarContext.Provider
|
|
value={{
|
|
collapsed,
|
|
toggleCollapsed: () => setCollapsed((value) => !value),
|
|
mobileOpen,
|
|
setMobileOpen,
|
|
isMobile,
|
|
}}
|
|
>
|
|
{children}
|
|
</SidebarContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useSidebar(): SidebarContextValue {
|
|
const context = useContext(SidebarContext);
|
|
|
|
if (!context) {
|
|
throw new Error('useSidebar must be used within SidebarProvider');
|
|
}
|
|
|
|
return context;
|
|
}
|