@@ -9,11 +9,13 @@ import { NavUser } from '@/components/layout/nav-user'
99import { useTheme } from '@/components/common/theme-provider'
1010import { ThemeToggle } from '@/components/common/theme-toggle'
1111import { Sidebar , SidebarContent , SidebarFooter , SidebarHeader , SidebarMenu , SidebarMenuButton , SidebarMenuItem , SidebarRail , useSidebar } from '@/components/ui/sidebar'
12- import { TooltipProvider } from '@/components/ui/tooltip'
12+ import { Tooltip , TooltipContent , TooltipProvider , TooltipTrigger } from '@/components/ui/tooltip'
13+ import { Button } from '@/components/ui/button'
1314import { Link } from 'react-router'
1415import { DISCUSSION_GROUP , DOCUMENTATION , DONATION_URL , REPO_URL } from '@/constants/Project'
1516import { useAdmin } from '@/hooks/use-admin'
1617import useDirDetection from '@/hooks/use-dir-detection'
18+ import { useVersionCheck } from '@/hooks/use-version-check'
1719import { cn } from '@/lib/utils'
1820import { getSystemStats } from '@/service/api'
1921import {
@@ -33,6 +35,8 @@ import {
3335 Lock ,
3436 MessageCircle ,
3537 Palette ,
38+ PanelLeftClose ,
39+ PanelLeftOpen ,
3640 PieChart ,
3741 RssIcon ,
3842 Send ,
@@ -54,8 +58,11 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
5458 const { t } = useTranslation ( )
5559 const [ version , setVersion ] = useState < string > ( '' )
5660 const { admin } = useAdmin ( )
57- const { setOpenMobile, openMobile, state, isMobile } = useSidebar ( )
61+ const { setOpenMobile, openMobile, state, isMobile, toggleSidebar } = useSidebar ( )
5862 const { resolvedTheme } = useTheme ( )
63+ const [ showCollapseButton , setShowCollapseButton ] = useState ( false )
64+ const currentVersion = version . replace ( / [ ^ 0 - 9 . ] / g, '' ) || null
65+ const { hasUpdate } = useVersionCheck ( currentVersion )
5966 const touchStartX = useRef < number | null > ( null )
6067 const touchEndX = useRef < number | null > ( null )
6168 const minSwipeDistance = 50
@@ -331,21 +338,108 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
331338 < SidebarMenu >
332339 < SidebarMenuItem >
333340 { state === 'collapsed' && ! isMobile ? (
334- < SidebarMenuButton size = "lg" asChild className = "relative" >
335- < a href = { REPO_URL } target = "_blank" className = "justify-center !gap-0" >
341+ < div
342+ className = "relative group"
343+ onMouseEnter = { ( ) => setShowCollapseButton ( true ) }
344+ onMouseLeave = { ( ) => setShowCollapseButton ( false ) }
345+ >
346+ { /* Badge - always visible, positioned on top layer */ }
347+ < div className = "absolute inset-0 pointer-events-none z-30" >
348+ < div className = "relative w-full h-full" >
349+ < VersionBadge currentVersion = { currentVersion } />
350+ </ div >
351+ </ div >
352+ { /* Logo - fades out on hover */ }
353+ < SidebarMenuButton
354+ size = "lg"
355+ asChild
356+ className = { cn (
357+ "relative justify-center !gap-0 w-full transition-opacity duration-200 ease-in-out" ,
358+ showCollapseButton ? "opacity-0 pointer-events-none" : "opacity-100"
359+ ) }
360+ >
361+ < a href = { REPO_URL } target = "_blank" >
362+ < img
363+ src = { resolvedTheme === 'dark' ? window . location . pathname + 'statics/favicon/logo.png' : window . location . pathname + 'statics/favicon/logo-dark.png' }
364+ alt = "PasarGuard Logo"
365+ className = "h-6 w-6 flex-shrink-0 object-contain"
366+ />
367+ { hasUpdate && (
368+ < TooltipProvider >
369+ < VersionBadge currentVersion = { currentVersion } />
370+ </ TooltipProvider >
371+ ) }
372+ </ a >
373+ </ SidebarMenuButton >
374+ { /* Expand button - fades in on hover */ }
375+ < TooltipProvider >
376+ < Tooltip >
377+ < TooltipTrigger asChild >
378+ < SidebarMenuButton
379+ size = "lg"
380+ className = { cn (
381+ "absolute inset-0 justify-center !gap-0 w-full transition-opacity duration-200 ease-in-out" ,
382+ showCollapseButton ? "opacity-100" : "opacity-0 pointer-events-none"
383+ ) }
384+ onClick = { toggleSidebar }
385+ >
386+ < PanelLeftOpen className = { cn ( "h-6 w-6 flex-shrink-0" , isRTL && "scale-x-[-1]" ) } />
387+ < span className = "sr-only" > Expand Sidebar</ span >
388+ { hasUpdate && < VersionBadge currentVersion = { currentVersion } /> }
389+ </ SidebarMenuButton >
390+ </ TooltipTrigger >
391+ < TooltipContent side = { isRTL ? 'left' : 'right' } >
392+ < p > { t ( 'sidebar.expand' ) } </ p >
393+ </ TooltipContent >
394+ </ Tooltip >
395+ </ TooltipProvider >
396+ </ div >
397+ ) : state !== 'collapsed' && ! isMobile ? (
398+ < SidebarMenuButton size = "lg" className = { cn ( 'relative !gap-2' , isRTL ? 'pl-10' : 'pr-10' ) } >
399+ < a href = { REPO_URL } target = "_blank" className = "flex items-center gap-2 flex-1 min-w-0" >
336400 < img
337401 src = { resolvedTheme === 'dark' ? window . location . pathname + 'statics/favicon/logo.png' : window . location . pathname + 'statics/favicon/logo-dark.png' }
338402 alt = "PasarGuard Logo"
339- className = "h-6 w-6 flex-shrink-0 object-contain"
403+ className = "h-8 w-8 flex-shrink-0 object-contain"
340404 />
341- < TooltipProvider >
342- < VersionBadge currentVersion = { version . replace ( / [ ^ 0 - 9 . ] / g, '' ) } />
343- </ TooltipProvider >
405+ < div className = "flex flex-col overflow-hidden min-w-0 flex-1" >
406+ < span className = { cn ( isRTL ? 'text-right' : 'text-left' , 'truncate text-sm font-semibold leading-tight' ) } > { t ( 'pasarguard' ) } </ span >
407+ < div className = "flex items-center gap-1.5 min-w-0" >
408+ < span className = "text-xs opacity-45 shrink-0" > { version } </ span >
409+ < div className = "min-w-0 flex-1" >
410+ < TooltipProvider >
411+ < VersionBadge currentVersion = { currentVersion } />
412+ </ TooltipProvider >
413+ </ div >
414+ </ div >
415+ </ div >
344416 </ a >
417+ < TooltipProvider >
418+ < Tooltip >
419+ < TooltipTrigger asChild >
420+ < Button
421+ variant = "ghost"
422+ size = "icon"
423+ className = { cn ( 'absolute h-8 w-8 shrink-0 z-10' , isRTL ? 'left-2' : 'right-2' ) }
424+ onClick = { ( e ) => {
425+ e . preventDefault ( )
426+ e . stopPropagation ( )
427+ toggleSidebar ( )
428+ } }
429+ >
430+ < PanelLeftClose className = { cn ( "h-4 w-4" , isRTL && "scale-x-[-1]" ) } />
431+ < span className = "sr-only" > Collapse Sidebar</ span >
432+ </ Button >
433+ </ TooltipTrigger >
434+ < TooltipContent side = { isRTL ? 'left' : 'right' } >
435+ < p > { t ( 'sidebar.collapse' ) } </ p >
436+ </ TooltipContent >
437+ </ Tooltip >
438+ </ TooltipProvider >
345439 </ SidebarMenuButton >
346440 ) : (
347- < SidebarMenuButton size = "lg" asChild >
348- < a href = { REPO_URL } target = "_blank" className = "!gap-2" >
441+ < SidebarMenuButton size = "lg" asChild className = "!gap-2" >
442+ < a href = { REPO_URL } target = "_blank" >
349443 < img
350444 src = { resolvedTheme === 'dark' ? window . location . pathname + 'statics/favicon/logo.png' : window . location . pathname + 'statics/favicon/logo-dark.png' }
351445 alt = "PasarGuard Logo"
0 commit comments