"use client" import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { Slot } from "@/lib/slot" import type { ValueOf } from "@/types/utils" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Separator } from "@/components/ui/separator" import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from "@/components/ui/sheet" import { Skeleton } from "@/components/ui/skeleton" import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip" import { useIsMobile } from "@/hooks/use-mobile" import { PanelLeftIcon } from "lucide-react" import { SidebarContext, useSidebar } from "./sidebar-context" const SIDEBAR_WIDTH = "16rem" const SIDEBAR_WIDTH_MOBILE = "18rem" const SIDEBAR_WIDTH_ICON = "3rem" const SIDEBAR_STATE = { EXPANDED: "expanded", COLLAPSED: "collapsed", } as const type SidebarState = ValueOf const SIDEBAR_SIDE = { LEFT: "left", RIGHT: "right", } as const type SidebarSide = ValueOf const SIDEBAR_VARIANT = { SIDEBAR: "sidebar", FLOATING: "floating", INSET: "inset", } as const type SidebarVariant = ValueOf const SIDEBAR_COLLAPSIBLE = { OFF_EXAMPLES: "offExamples", ICON: "icon", NONE: "none", } as const type SidebarCollapsible = ValueOf const SIDEBAR_MENU_SUB_BUTTON_SIZE = { SMALL: "sm", MEDIUM: "md", } as const type SidebarMenuSubButtonSize = ValueOf function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }: React.ComponentProps<"div"> & { defaultOpen?: boolean open?: boolean onOpenChange?: (open: boolean) => void }) { const isMobile = useIsMobile() const [openMobile, setOpenMobile] = React.useState(false) // This is the internal state of the sidebar. // We use openProp and setOpenProp for control from outside the component. const [_open, _setOpen] = React.useState(defaultOpen) const open = openProp ?? _open const setOpen = (value: boolean | ((value: boolean) => boolean)) => { const openState = typeof value === "function" ? value(open) : value if (setOpenProp) { setOpenProp(openState) } else { _setOpen(openState) } } // Helper to toggle the sidebar. const toggleSidebar = () => { return isMobile ? setOpenMobile((value) => !value) : setOpen((value) => !value) } // We add a state so that we can do data-state="expanded" or "collapsed". // This makes it easier to style the sidebar with Tailwind classes. const state: SidebarState = open ? SIDEBAR_STATE.EXPANDED : SIDEBAR_STATE.COLLAPSED const contextValue = { state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar, } return (
{children}
) } function Sidebar({ side = SIDEBAR_SIDE.LEFT, variant = SIDEBAR_VARIANT.SIDEBAR, collapsible = SIDEBAR_COLLAPSIBLE.OFF_EXAMPLES, className, children, ...props }: React.ComponentProps<"div"> & { side?: SidebarSide variant?: SidebarVariant collapsible?: SidebarCollapsible }) { const { isMobile, state, openMobile, setOpenMobile } = useSidebar() if (collapsible === SIDEBAR_COLLAPSIBLE.NONE) { return (
{children}
) } if (isMobile) { return ( Sidebar Displays the mobile sidebar.
{children}
) } return (
{/* This is what handles the sidebar gap on desktop */}
{children}
) } function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps) { const { toggleSidebar } = useSidebar() return ( ) } function SidebarRail({ className, ...props }: React.ComponentProps<"button">) { const { toggleSidebar } = useSidebar() return (