import { useState, useCallback, type ReactNode } from "react";
import { Button } from "@/src/components/ui/button";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { SubHeader } from "@/src/components/layouts/header";
import { cn } from "@/src/utils/tailwind";
import React from "react";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/src/components/ui/sheet";
import { useIsMobile } from "@/src/hooks/use-mobile";
import { Separator } from "@/src/components/ui/separator";
import useSessionStorage from "@/src/components/useSessionStorage";
const SidePanelContext = React.createContext<{
showPanel: boolean;
setShowPanel: (value: boolean) => void;
isMobile: boolean;
isControlled: boolean;
} | null>(null);
/**
* Side panel component with controlled/uncontrolled modes.
*
* **Uncontrolled mode** (default):
* - Omit `openState` prop
* - Panel state persisted in session storage
* - User can toggle via chevron button
* - Example: `{content}`
*
* **Controlled mode**:
* - Provide `openState` with `open` and `onOpenChange`
* - No session storage, no toggle button
* - Parent controls open/close state
* - Example: `{content}`
*/
const SidePanel = ({
id,
children,
className,
mobileTitle,
scrollable = true,
openState,
}: {
id: string;
children: ReactNode;
className?: string;
mobileTitle?: string;
scrollable?: boolean;
/** Controlled mode: provide to control panel state externally */
openState?: {
open: boolean;
onOpenChange: (open: boolean) => void;
};
}) => {
const isControlled = openState !== undefined;
const controlledOpen = openState?.open;
const onOpenChange = openState?.onOpenChange;
const [uncontrolledOpen, setUncontrolledOpen] = useSessionStorage(
`${id}-showPanel`,
true,
);
const [mobileSheetOpen, setMobileSheetOpen] = useState(false);
const isMobile = useIsMobile();
const showPanel = isControlled ? (controlledOpen ?? false) : uncontrolledOpen;
const setShowPanel = useCallback(
(value: boolean) => {
if (isControlled) {
onOpenChange?.(value);
} else {
setUncontrolledOpen(value);
}
},
[isControlled, onOpenChange, setUncontrolledOpen],
);
const handleMobileOpenChange = useCallback(
(open: boolean) => {
if (isControlled) {
onOpenChange?.(open);
} else {
setMobileSheetOpen(open);
}
},
[isControlled, onOpenChange],
);
const contextValue = React.useMemo(
() => ({
showPanel,
setShowPanel,
isMobile,
isControlled,
}),
[showPanel, setShowPanel, isMobile, isControlled],
);
if (isMobile) {
return (
{!isControlled && (
)}
{mobileTitle}
{children}
);
}
return (
);
};
const SidePanelHeader = ({ children }: { children: ReactNode }) => {
const context = React.useContext(SidePanelContext);
if (!context) return null;
const { showPanel, setShowPanel, isControlled } = context;
if (isControlled) {
if (!showPanel) return null;
return (
);
}
if (!showPanel) {
return (
);
}
return (
{children}
);
};
const SidePanelTitle = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => ;
const SidePanelContent = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => {
const context = React.useContext(SidePanelContext);
if (!context) return children;
const { showPanel } = context;
if (!showPanel) return null;
return (
{children}
);
};
export { SidePanel, SidePanelHeader, SidePanelTitle, SidePanelContent };