(front) add LeftPanelContext for mobile panel state

Introduce a context to manage left panel visibility on
mobile, integrate it into SimpleLayout and calendar page.
This commit is contained in:
Nathan Panchout
2026-03-11 10:43:20 +01:00
parent 75f26b59d2
commit e7446788f2
4 changed files with 65 additions and 4 deletions

View File

@@ -3,6 +3,7 @@ import { GlobalLayout } from "../global/GlobalLayout";
import { HeaderRight } from "../header/Header"; import { HeaderRight } from "../header/Header";
import { Toaster } from "@/features/ui/components/toaster/Toaster"; import { Toaster } from "@/features/ui/components/toaster/Toaster";
import { LeftPanelMobile } from "@/features/layouts/components/left-panel/LeftPanelMobile"; import { LeftPanelMobile } from "@/features/layouts/components/left-panel/LeftPanelMobile";
import { useLeftPanel } from "../../contexts/LeftPanelContext";
export const getSimpleLayout = (page: React.ReactElement) => { export const getSimpleLayout = (page: React.ReactElement) => {
return <SimpleLayout>{page}</SimpleLayout>; return <SimpleLayout>{page}</SimpleLayout>;
@@ -14,12 +15,15 @@ export const getSimpleLayout = (page: React.ReactElement) => {
* Auth context to the children. * Auth context to the children.
*/ */
export const SimpleLayout = ({ children }: { children: React.ReactNode }) => { export const SimpleLayout = ({ children }: { children: React.ReactNode }) => {
const { isLeftPanelOpen, setIsLeftPanelOpen } = useLeftPanel();
return ( return (
<div> <div>
<GlobalLayout> <GlobalLayout>
<MainLayout <MainLayout
enableResize enableResize
hideLeftPanelOnDesktop={true} hideLeftPanelOnDesktop={true}
isLeftPanelOpen={isLeftPanelOpen}
setIsLeftPanelOpen={setIsLeftPanelOpen}
leftPanelContent={<LeftPanelMobile />} leftPanelContent={<LeftPanelMobile />}
rightHeaderContent={<HeaderRight />} rightHeaderContent={<HeaderRight />}
> >

View File

@@ -0,0 +1,45 @@
import {
createContext,
useContext,
useMemo,
useState,
type ReactNode,
} from "react";
interface LeftPanelContextType {
isLeftPanelOpen: boolean;
setIsLeftPanelOpen: (open: boolean) => void;
}
const LeftPanelContext = createContext<LeftPanelContextType | undefined>(
undefined,
);
export const useLeftPanel = () => {
const context = useContext(LeftPanelContext);
if (!context) {
throw new Error(
"useLeftPanel must be used within a LeftPanelProvider",
);
}
return context;
};
interface LeftPanelProviderProps {
children: ReactNode;
}
export const LeftPanelProvider = ({ children }: LeftPanelProviderProps) => {
const [isLeftPanelOpen, setIsLeftPanelOpen] = useState(false);
const value = useMemo(
() => ({ isLeftPanelOpen, setIsLeftPanelOpen }),
[isLeftPanelOpen, setIsLeftPanelOpen],
);
return (
<LeftPanelContext.Provider value={value}>
{children}
</LeftPanelContext.Provider>
);
};

View File

@@ -33,6 +33,7 @@ import {
} from "@/features/ui/cunningham/useCunninghamTheme"; } from "@/features/ui/cunningham/useCunninghamTheme";
import { FeedbackFooterMobile } from "@/features/feedback/Feedback"; import { FeedbackFooterMobile } from "@/features/feedback/Feedback";
import { useDynamicFavicon } from "@/features/ui/hooks/useDynamicFavicon"; import { useDynamicFavicon } from "@/features/ui/hooks/useDynamicFavicon";
import { LeftPanelProvider } from "@/features/layouts/contexts/LeftPanelContext";
export type NextPageWithLayout<P = object, IP = P> = NextPage<P, IP> & { export type NextPageWithLayout<P = object, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode; getLayout?: (page: ReactElement) => ReactNode;
@@ -134,8 +135,10 @@ const MyAppInner = ({ Component, pageProps }: AppPropsWithLayout) => {
<ConfigProvider> <ConfigProvider>
<AnalyticsProvider> <AnalyticsProvider>
{getLayout(<Component {...pageProps} />)} <LeftPanelProvider>
<FeedbackFooterMobile /> {getLayout(<Component {...pageProps} />)}
<FeedbackFooterMobile />
</LeftPanelProvider>
</AnalyticsProvider> </AnalyticsProvider>
</ConfigProvider> </ConfigProvider>

View File

@@ -15,6 +15,7 @@ import {
HeaderIcon, HeaderIcon,
HeaderRight, HeaderRight,
} from "@/features/layouts/components/header/Header"; } from "@/features/layouts/components/header/Header";
import { useLeftPanel } from "@/features/layouts/contexts/LeftPanelContext";
import { SpinnerPage } from "@/features/ui/components/spinner/SpinnerPage"; import { SpinnerPage } from "@/features/ui/components/spinner/SpinnerPage";
import { Toaster } from "@/features/ui/components/toaster/Toaster"; import { Toaster } from "@/features/ui/components/toaster/Toaster";
import { Scheduler } from "@/features/calendar/components/scheduler/Scheduler"; import { Scheduler } from "@/features/calendar/components/scheduler/Scheduler";
@@ -56,7 +57,9 @@ export default function CalendarPage() {
); );
} }
CalendarPage.getLayout = function getLayout(page: React.ReactElement) { const CalendarLayout = ({ children }: { children: React.ReactNode }) => {
const { isLeftPanelOpen, setIsLeftPanelOpen } = useLeftPanel();
return ( return (
<CalendarContextProvider> <CalendarContextProvider>
<div className="calendars__calendar"> <div className="calendars__calendar">
@@ -66,11 +69,17 @@ CalendarPage.getLayout = function getLayout(page: React.ReactElement) {
leftPanelContent={<LeftPanel />} leftPanelContent={<LeftPanel />}
icon={<HeaderIcon />} icon={<HeaderIcon />}
rightHeaderContent={<HeaderRight />} rightHeaderContent={<HeaderRight />}
isLeftPanelOpen={isLeftPanelOpen}
setIsLeftPanelOpen={setIsLeftPanelOpen}
> >
{page} {children}
</MainLayout> </MainLayout>
</GlobalLayout> </GlobalLayout>
</div> </div>
</CalendarContextProvider> </CalendarContextProvider>
); );
}; };
CalendarPage.getLayout = function getLayout(page: React.ReactElement) {
return <CalendarLayout>{page}</CalendarLayout>;
};