1- "use client"
2-
3- import * as React from "react"
41import { Slot } from "@radix-ui/react-slot"
52import { cva , type VariantProps } from "class-variance-authority"
63import { PanelLeftIcon } from "lucide-react"
4+ import * as React from "react"
75
8- import { useIsMobile } from "@/hooks/use-mobile"
9- import { cn } from "@/lib/utils"
106import { Button } from "@/components/ui/button"
117import { Input } from "@/components/ui/input"
128import { Separator } from "@/components/ui/separator"
@@ -24,6 +20,8 @@ import {
2420 TooltipProvider ,
2521 TooltipTrigger ,
2622} from "@/components/ui/tooltip"
23+ import { cn } from "@/lib/utils"
24+ import { useIsMobile } from "@/hooks/useMobile"
2725
2826const SIDEBAR_COOKIE_NAME = "sidebar_state"
2927const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
@@ -69,9 +67,21 @@ function SidebarProvider({
6967 const isMobile = useIsMobile ( )
7068 const [ openMobile , setOpenMobile ] = React . useState ( false )
7169
70+ const getInitialOpen = ( ) => {
71+ if ( typeof document === "undefined" ) return defaultOpen
72+
73+ const cookie = document . cookie
74+ . split ( "; " )
75+ . find ( ( c ) => c . startsWith ( `${ SIDEBAR_COOKIE_NAME } =` ) )
76+
77+ if ( ! cookie ) return defaultOpen
78+
79+ return cookie . split ( "=" ) [ 1 ] === "true"
80+ }
81+
7282 // This is the internal state of the sidebar.
7383 // We use openProp and setOpenProp for control from outside the component.
74- const [ _open , _setOpen ] = React . useState ( defaultOpen )
84+ const [ _open , _setOpen ] = React . useState ( getInitialOpen )
7585 const open = openProp ?? _open
7686 const setOpen = React . useCallback (
7787 ( value : boolean | ( ( value : boolean ) => boolean ) ) => {
@@ -85,13 +95,13 @@ function SidebarProvider({
8595 // This sets the cookie to keep the sidebar state.
8696 document . cookie = `${ SIDEBAR_COOKIE_NAME } =${ openState } ; path=/; max-age=${ SIDEBAR_COOKIE_MAX_AGE } `
8797 } ,
88- [ setOpenProp , open ]
98+ [ setOpenProp , open ] ,
8999 )
90100
91101 // Helper to toggle the sidebar.
92102 const toggleSidebar = React . useCallback ( ( ) => {
93103 return isMobile ? setOpenMobile ( ( open ) => ! open ) : setOpen ( ( open ) => ! open )
94- } , [ isMobile , setOpen , setOpenMobile ] )
104+ } , [ isMobile , setOpen ] )
95105
96106 // Adds a keyboard shortcut to toggle the sidebar.
97107 React . useEffect ( ( ) => {
@@ -123,7 +133,7 @@ function SidebarProvider({
123133 setOpenMobile,
124134 toggleSidebar,
125135 } ) ,
126- [ state , open , setOpen , isMobile , openMobile , setOpenMobile , toggleSidebar ]
136+ [ state , open , setOpen , isMobile , openMobile , toggleSidebar ] ,
127137 )
128138
129139 return (
@@ -140,7 +150,7 @@ function SidebarProvider({
140150 }
141151 className = { cn (
142152 "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full" ,
143- className
153+ className ,
144154 ) }
145155 { ...props }
146156 >
@@ -171,7 +181,7 @@ function Sidebar({
171181 data-slot = "sidebar"
172182 className = { cn (
173183 "bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col" ,
174- className
184+ className ,
175185 ) }
176186 { ...props }
177187 >
@@ -223,7 +233,7 @@ function Sidebar({
223233 "group-data-[side=right]:rotate-180" ,
224234 variant === "floating" || variant === "inset"
225235 ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
226- : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
236+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)" ,
227237 ) }
228238 />
229239 < div
@@ -237,7 +247,7 @@ function Sidebar({
237247 variant === "floating" || variant === "inset"
238248 ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
239249 : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l" ,
240- className
250+ className ,
241251 ) }
242252 { ...props }
243253 >
@@ -258,7 +268,8 @@ function SidebarTrigger({
258268 onClick,
259269 ...props
260270} : React . ComponentProps < typeof Button > ) {
261- const { toggleSidebar } = useSidebar ( )
271+ const { toggleSidebar, open } = useSidebar ( )
272+ const sidebarCopy = open ? "Collapse Sidebar" : "Open Sidebar"
262273
263274 return (
264275 < Button
@@ -274,7 +285,7 @@ function SidebarTrigger({
274285 { ...props }
275286 >
276287 < PanelLeftIcon />
277- < span className = "sr-only" > Toggle Sidebar </ span >
288+ < span className = "sr-only" > { sidebarCopy } </ span >
278289 </ Button >
279290 )
280291}
@@ -297,7 +308,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
297308 "hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full" ,
298309 "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2" ,
299310 "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2" ,
300- className
311+ className ,
301312 ) }
302313 { ...props }
303314 />
@@ -309,9 +320,9 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
309320 < main
310321 data-slot = "sidebar-inset"
311322 className = { cn (
312- "bg-background relative flex w-full flex-1 flex-col" ,
323+ "bg-transparent relative flex w-full flex-1 flex-col" ,
313324 "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2" ,
314- className
325+ className ,
315326 ) }
316327 { ...props }
317328 />
@@ -337,7 +348,7 @@ function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
337348 < div
338349 data-slot = "sidebar-header"
339350 data-sidebar = "header"
340- className = { cn ( "flex flex-col gap-2 p-2" , className ) }
351+ className = { cn ( "flex flex-col gap-2 p-2 pb-3 " , className ) }
341352 { ...props }
342353 />
343354 )
@@ -348,7 +359,7 @@ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
348359 < div
349360 data-slot = "sidebar-footer"
350361 data-sidebar = "footer"
351- className = { cn ( "flex flex-col gap-2 p-2" , className ) }
362+ className = { cn ( "flex flex-col gap-3 p-2" , className ) }
352363 { ...props }
353364 />
354365 )
@@ -375,7 +386,7 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
375386 data-sidebar = "content"
376387 className = { cn (
377388 "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden" ,
378- className
389+ className ,
379390 ) }
380391 { ...props }
381392 />
@@ -387,7 +398,7 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
387398 < div
388399 data-slot = "sidebar-group"
389400 data-sidebar = "group"
390- className = { cn ( "relative flex w-full min-w-0 flex-col p -2" , className ) }
401+ className = { cn ( "relative flex w-full min-w-0 flex-col px -2" , className ) }
391402 { ...props }
392403 />
393404 )
@@ -407,7 +418,7 @@ function SidebarGroupLabel({
407418 className = { cn (
408419 "text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0" ,
409420 "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0" ,
410- className
421+ className ,
411422 ) }
412423 { ...props }
413424 />
@@ -430,7 +441,7 @@ function SidebarGroupAction({
430441 // Increases the hit area of the button on mobile.
431442 "after:absolute after:-inset-2 md:after:hidden" ,
432443 "group-data-[collapsible=icon]:hidden" ,
433- className
444+ className ,
434445 ) }
435446 { ...props }
436447 />
@@ -474,7 +485,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
474485}
475486
476487const sidebarMenuButtonVariants = cva (
477- "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0" ,
488+ "peer/menu-button flex w-full items-center gap-2 overflow-hidden p-2 text-left text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 data-[state=open]:bg-sidebar-accent " ,
478489 {
479490 variants : {
480491 variant : {
@@ -483,16 +494,16 @@ const sidebarMenuButtonVariants = cva(
483494 "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]" ,
484495 } ,
485496 size : {
486- default : "h-8 text-sm" ,
497+ default : "h-8 text-sm rounded-lg " ,
487498 sm : "h-7 text-xs" ,
488- lg : "h-12 text-sm group-data-[collapsible=icon]:p-0!" ,
499+ lg : "h-12 text-sm group-data-[collapsible=icon]:p-0! rounded-xl data-[state=closed]:rounded-lg " ,
489500 } ,
490501 } ,
491502 defaultVariants : {
492503 variant : "default" ,
493504 size : "default" ,
494505 } ,
495- }
506+ } ,
496507)
497508
498509function SidebarMenuButton ( {
@@ -569,8 +580,8 @@ function SidebarMenuAction({
569580 "peer-data-[size=lg]/menu-button:top-2.5" ,
570581 "group-data-[collapsible=icon]:hidden" ,
571582 showOnHover &&
572- "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0" ,
573- className
583+ "peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0" ,
584+ className ,
574585 ) }
575586 { ...props }
576587 />
@@ -592,7 +603,7 @@ function SidebarMenuBadge({
592603 "peer-data-[size=default]/menu-button:top-1.5" ,
593604 "peer-data-[size=lg]/menu-button:top-2.5" ,
594605 "group-data-[collapsible=icon]:hidden" ,
595- className
606+ className ,
596607 ) }
597608 { ...props }
598609 />
@@ -645,7 +656,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
645656 className = { cn (
646657 "border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5" ,
647658 "group-data-[collapsible=icon]:hidden" ,
648- className
659+ className ,
649660 ) }
650661 { ...props }
651662 />
@@ -691,7 +702,7 @@ function SidebarMenuSubButton({
691702 size === "sm" && "text-xs" ,
692703 size === "md" && "text-sm" ,
693704 "group-data-[collapsible=icon]:hidden" ,
694- className
705+ className ,
695706 ) }
696707 { ...props }
697708 />
0 commit comments