stunning screenshots in seconds https://moocup.jaydip.me
1import * as React from "react" 2import { Slot } from "@radix-ui/react-slot" 3import { VariantProps, cva } from "class-variance-authority" 4import { PanelLeft } from "lucide-react" 5 6import { useIsMobile } from "@/hooks/use-mobile" 7import { cn } from "@/lib/utils" 8import { Button } from "@/components/ui/button" 9import { Input } from "@/components/ui/input" 10import { Separator } from "@/components/ui/separator" 11import { Sheet, SheetContent } from "@/components/ui/sheet" 12import { Skeleton } from "@/components/ui/skeleton" 13import { 14 Tooltip, 15 TooltipContent, 16 TooltipProvider, 17 TooltipTrigger, 18} from "@/components/ui/tooltip" 19 20const SIDEBAR_COOKIE_NAME = "sidebar:state" 21const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 22const SIDEBAR_WIDTH = "16rem" 23const SIDEBAR_WIDTH_MOBILE = "18rem" 24const SIDEBAR_WIDTH_ICON = "3rem" 25const SIDEBAR_KEYBOARD_SHORTCUT = "b" 26 27type SidebarContext = { 28 state: "expanded" | "collapsed" 29 open: boolean 30 setOpen: (open: boolean) => void 31 openMobile: boolean 32 setOpenMobile: (open: boolean) => void 33 isMobile: boolean 34 toggleSidebar: () => void 35} 36 37const SidebarContext = React.createContext<SidebarContext | null>(null) 38 39function useSidebar() { 40 const context = React.useContext(SidebarContext) 41 if (!context) { 42 throw new Error("useSidebar must be used within a SidebarProvider.") 43 } 44 45 return context 46} 47 48const SidebarProvider = React.forwardRef< 49 HTMLDivElement, 50 React.ComponentProps<"div"> & { 51 defaultOpen?: boolean 52 open?: boolean 53 onOpenChange?: (open: boolean) => void 54 } 55>( 56 ( 57 { 58 defaultOpen = true, 59 open: openProp, 60 onOpenChange: setOpenProp, 61 className, 62 style, 63 children, 64 ...props 65 }, 66 ref 67 ) => { 68 const isMobile = useIsMobile() 69 const [openMobile, setOpenMobile] = React.useState(false) 70 71 // This is the internal state of the sidebar. 72 // We use openProp and setOpenProp for control from outside the component. 73 const [_open, _setOpen] = React.useState(defaultOpen) 74 const open = openProp ?? _open 75 const setOpen = React.useCallback( 76 (value: boolean | ((value: boolean) => boolean)) => { 77 const openState = typeof value === "function" ? value(open) : value 78 if (setOpenProp) { 79 setOpenProp(openState) 80 } else { 81 _setOpen(openState) 82 } 83 84 // This sets the cookie to keep the sidebar state. 85 document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` 86 }, 87 [setOpenProp, open] 88 ) 89 90 // Helper to toggle the sidebar. 91 const toggleSidebar = React.useCallback(() => { 92 return isMobile 93 ? setOpenMobile((open) => !open) 94 : setOpen((open) => !open) 95 }, [isMobile, setOpen, setOpenMobile]) 96 97 // Adds a keyboard shortcut to toggle the sidebar. 98 React.useEffect(() => { 99 const handleKeyDown = (event: KeyboardEvent) => { 100 if ( 101 event.key === SIDEBAR_KEYBOARD_SHORTCUT && 102 (event.metaKey || event.ctrlKey) 103 ) { 104 event.preventDefault() 105 toggleSidebar() 106 } 107 } 108 109 window.addEventListener("keydown", handleKeyDown) 110 return () => window.removeEventListener("keydown", handleKeyDown) 111 }, [toggleSidebar]) 112 113 // We add a state so that we can do data-state="expanded" or "collapsed". 114 // This makes it easier to style the sidebar with Tailwind classes. 115 const state = open ? "expanded" : "collapsed" 116 117 const contextValue = React.useMemo<SidebarContext>( 118 () => ({ 119 state, 120 open, 121 setOpen, 122 isMobile, 123 openMobile, 124 setOpenMobile, 125 toggleSidebar, 126 }), 127 [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] 128 ) 129 130 return ( 131 <SidebarContext.Provider value={contextValue}> 132 <TooltipProvider delayDuration={0}> 133 <div 134 style={ 135 { 136 "--sidebar-width": SIDEBAR_WIDTH, 137 "--sidebar-width-icon": SIDEBAR_WIDTH_ICON, 138 ...style, 139 } as React.CSSProperties 140 } 141 className={cn( 142 "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar", 143 className 144 )} 145 ref={ref} 146 {...props} 147 > 148 {children} 149 </div> 150 </TooltipProvider> 151 </SidebarContext.Provider> 152 ) 153 } 154) 155SidebarProvider.displayName = "SidebarProvider" 156 157const Sidebar = React.forwardRef< 158 HTMLDivElement, 159 React.ComponentProps<"div"> & { 160 side?: "left" | "right" 161 variant?: "sidebar" | "floating" | "inset" 162 collapsible?: "offcanvas" | "icon" | "none" 163 } 164>( 165 ( 166 { 167 side = "left", 168 variant = "sidebar", 169 collapsible = "offcanvas", 170 className, 171 children, 172 ...props 173 }, 174 ref 175 ) => { 176 const { isMobile, state, openMobile, setOpenMobile } = useSidebar() 177 178 if (collapsible === "none") { 179 return ( 180 <div 181 className={cn( 182 "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground", 183 className 184 )} 185 ref={ref} 186 {...props} 187 > 188 {children} 189 </div> 190 ) 191 } 192 193 if (isMobile) { 194 return ( 195 <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}> 196 <SheetContent 197 data-sidebar="sidebar" 198 data-mobile="true" 199 className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden" 200 style={ 201 { 202 "--sidebar-width": SIDEBAR_WIDTH_MOBILE, 203 } as React.CSSProperties 204 } 205 side={side} 206 > 207 <div className="flex h-full w-full flex-col">{children}</div> 208 </SheetContent> 209 </Sheet> 210 ) 211 } 212 213 return ( 214 <div 215 ref={ref} 216 className="group peer hidden md:block text-sidebar-foreground" 217 data-state={state} 218 data-collapsible={state === "collapsed" ? collapsible : ""} 219 data-variant={variant} 220 data-side={side} 221 > 222 {/* This is what handles the sidebar gap on desktop */} 223 <div 224 className={cn( 225 "duration-200 relative h-svh w-(--sidebar-width) bg-transparent transition-[width] ease-linear", 226 "group-data-[collapsible=offcanvas]:w-0", 227 "group-data-[side=right]:rotate-180", 228 variant === "floating" || variant === "inset" 229 ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" 230 : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)" 231 )} 232 /> 233 <div 234 className={cn( 235 "duration-200 fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] ease-linear md:flex", 236 side === "left" 237 ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" 238 : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", 239 // Adjust the padding for floating and inset variants. 240 variant === "floating" || variant === "inset" 241 ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" 242 : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l", 243 className 244 )} 245 {...props} 246 > 247 <div 248 data-sidebar="sidebar" 249 className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm" 250 > 251 {children} 252 </div> 253 </div> 254 </div> 255 ) 256 } 257) 258Sidebar.displayName = "Sidebar" 259 260const SidebarTrigger = React.forwardRef< 261 React.ElementRef<typeof Button>, 262 React.ComponentProps<typeof Button> 263>(({ className, onClick, ...props }, ref) => { 264 const { toggleSidebar } = useSidebar() 265 266 return ( 267 <Button 268 ref={ref} 269 data-sidebar="trigger" 270 variant="ghost" 271 size="icon" 272 className={cn("h-7 w-7", className)} 273 onClick={(event) => { 274 onClick?.(event) 275 toggleSidebar() 276 }} 277 {...props} 278 > 279 <PanelLeft /> 280 <span className="sr-only">Toggle Sidebar</span> 281 </Button> 282 ) 283}) 284SidebarTrigger.displayName = "SidebarTrigger" 285 286const SidebarRail = React.forwardRef< 287 HTMLButtonElement, 288 React.ComponentProps<"button"> 289>(({ className, ...props }, ref) => { 290 const { toggleSidebar } = useSidebar() 291 292 return ( 293 <button 294 ref={ref} 295 data-sidebar="rail" 296 aria-label="Toggle Sidebar" 297 tabIndex={-1} 298 onClick={toggleSidebar} 299 title="Toggle Sidebar" 300 className={cn( 301 "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex", 302 "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", 303 "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", 304 "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar", 305 "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2", 306 "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2", 307 className 308 )} 309 {...props} 310 /> 311 ) 312}) 313SidebarRail.displayName = "SidebarRail" 314 315const SidebarInset = React.forwardRef< 316 HTMLDivElement, 317 React.ComponentProps<"main"> 318>(({ className, ...props }, ref) => { 319 return ( 320 <main 321 ref={ref} 322 className={cn( 323 "relative flex min-h-svh flex-1 flex-col bg-background", 324 "peer-data-[variant=inset]:min-h-[calc(100svh-(--spacing(4)))] md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm", 325 className 326 )} 327 {...props} 328 /> 329 ) 330}) 331SidebarInset.displayName = "SidebarInset" 332 333const SidebarInput = React.forwardRef< 334 React.ElementRef<typeof Input>, 335 React.ComponentProps<typeof Input> 336>(({ className, ...props }, ref) => { 337 return ( 338 <Input 339 ref={ref} 340 data-sidebar="input" 341 className={cn( 342 "h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring", 343 className 344 )} 345 {...props} 346 /> 347 ) 348}) 349SidebarInput.displayName = "SidebarInput" 350 351const SidebarHeader = React.forwardRef< 352 HTMLDivElement, 353 React.ComponentProps<"div"> 354>(({ className, ...props }, ref) => { 355 return ( 356 <div 357 ref={ref} 358 data-sidebar="header" 359 className={cn("flex flex-col gap-2 p-2", className)} 360 {...props} 361 /> 362 ) 363}) 364SidebarHeader.displayName = "SidebarHeader" 365 366const SidebarFooter = React.forwardRef< 367 HTMLDivElement, 368 React.ComponentProps<"div"> 369>(({ className, ...props }, ref) => { 370 return ( 371 <div 372 ref={ref} 373 data-sidebar="footer" 374 className={cn("flex flex-col gap-2 p-2", className)} 375 {...props} 376 /> 377 ) 378}) 379SidebarFooter.displayName = "SidebarFooter" 380 381const SidebarSeparator = React.forwardRef< 382 React.ElementRef<typeof Separator>, 383 React.ComponentProps<typeof Separator> 384>(({ className, ...props }, ref) => { 385 return ( 386 <Separator 387 ref={ref} 388 data-sidebar="separator" 389 className={cn("mx-2 w-auto bg-sidebar-border", className)} 390 {...props} 391 /> 392 ) 393}) 394SidebarSeparator.displayName = "SidebarSeparator" 395 396const SidebarContent = React.forwardRef< 397 HTMLDivElement, 398 React.ComponentProps<"div"> 399>(({ className, ...props }, ref) => { 400 return ( 401 <div 402 ref={ref} 403 data-sidebar="content" 404 className={cn( 405 "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", 406 className 407 )} 408 {...props} 409 /> 410 ) 411}) 412SidebarContent.displayName = "SidebarContent" 413 414const SidebarGroup = React.forwardRef< 415 HTMLDivElement, 416 React.ComponentProps<"div"> 417>(({ className, ...props }, ref) => { 418 return ( 419 <div 420 ref={ref} 421 data-sidebar="group" 422 className={cn("relative flex w-full min-w-0 flex-col p-2", className)} 423 {...props} 424 /> 425 ) 426}) 427SidebarGroup.displayName = "SidebarGroup" 428 429const SidebarGroupLabel = React.forwardRef< 430 HTMLDivElement, 431 React.ComponentProps<"div"> & { asChild?: boolean } 432>(({ className, asChild = false, ...props }, ref) => { 433 const Comp = asChild ? Slot : "div" 434 435 return ( 436 <Comp 437 ref={ref} 438 data-sidebar="group-label" 439 className={cn( 440 "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-hidden ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", 441 "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", 442 className 443 )} 444 {...props} 445 /> 446 ) 447}) 448SidebarGroupLabel.displayName = "SidebarGroupLabel" 449 450const SidebarGroupAction = React.forwardRef< 451 HTMLButtonElement, 452 React.ComponentProps<"button"> & { asChild?: boolean } 453>(({ className, asChild = false, ...props }, ref) => { 454 const Comp = asChild ? Slot : "button" 455 456 return ( 457 <Comp 458 ref={ref} 459 data-sidebar="group-action" 460 className={cn( 461 "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-hidden ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", 462 // Increases the hit area of the button on mobile. 463 "after:absolute after:-inset-2 md:after:hidden", 464 "group-data-[collapsible=icon]:hidden", 465 className 466 )} 467 {...props} 468 /> 469 ) 470}) 471SidebarGroupAction.displayName = "SidebarGroupAction" 472 473const SidebarGroupContent = React.forwardRef< 474 HTMLDivElement, 475 React.ComponentProps<"div"> 476>(({ className, ...props }, ref) => ( 477 <div 478 ref={ref} 479 data-sidebar="group-content" 480 className={cn("w-full text-sm", className)} 481 {...props} 482 /> 483)) 484SidebarGroupContent.displayName = "SidebarGroupContent" 485 486const SidebarMenu = React.forwardRef< 487 HTMLUListElement, 488 React.ComponentProps<"ul"> 489>(({ className, ...props }, ref) => ( 490 <ul 491 ref={ref} 492 data-sidebar="menu" 493 className={cn("flex w-full min-w-0 flex-col gap-1", className)} 494 {...props} 495 /> 496)) 497SidebarMenu.displayName = "SidebarMenu" 498 499const SidebarMenuItem = React.forwardRef< 500 HTMLLIElement, 501 React.ComponentProps<"li"> 502>(({ className, ...props }, ref) => ( 503 <li 504 ref={ref} 505 data-sidebar="menu-item" 506 className={cn("group/menu-item relative", className)} 507 {...props} 508 /> 509)) 510SidebarMenuItem.displayName = "SidebarMenuItem" 511 512const sidebarMenuButtonVariants = cva( 513 "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", 514 { 515 variants: { 516 variant: { 517 default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", 518 outline: 519 "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))]", 520 }, 521 size: { 522 default: "h-8 text-sm", 523 sm: "h-7 text-xs", 524 lg: "h-12 text-sm group-data-[collapsible=icon]:p-0!", 525 }, 526 }, 527 defaultVariants: { 528 variant: "default", 529 size: "default", 530 }, 531 } 532) 533 534const SidebarMenuButton = React.forwardRef< 535 HTMLButtonElement, 536 React.ComponentProps<"button"> & { 537 asChild?: boolean 538 isActive?: boolean 539 tooltip?: string | React.ComponentProps<typeof TooltipContent> 540 } & VariantProps<typeof sidebarMenuButtonVariants> 541>( 542 ( 543 { 544 asChild = false, 545 isActive = false, 546 variant = "default", 547 size = "default", 548 tooltip, 549 className, 550 ...props 551 }, 552 ref 553 ) => { 554 const Comp = asChild ? Slot : "button" 555 const { isMobile, state } = useSidebar() 556 557 const button = ( 558 <Comp 559 ref={ref} 560 data-sidebar="menu-button" 561 data-size={size} 562 data-active={isActive} 563 className={cn(sidebarMenuButtonVariants({ variant, size }), className)} 564 {...props} 565 /> 566 ) 567 568 if (!tooltip) { 569 return button 570 } 571 572 if (typeof tooltip === "string") { 573 tooltip = { 574 children: tooltip, 575 } 576 } 577 578 return ( 579 <Tooltip> 580 <TooltipTrigger asChild>{button}</TooltipTrigger> 581 <TooltipContent 582 side="right" 583 align="center" 584 hidden={state !== "collapsed" || isMobile} 585 {...tooltip} 586 /> 587 </Tooltip> 588 ) 589 } 590) 591SidebarMenuButton.displayName = "SidebarMenuButton" 592 593const SidebarMenuAction = React.forwardRef< 594 HTMLButtonElement, 595 React.ComponentProps<"button"> & { 596 asChild?: boolean 597 showOnHover?: boolean 598 } 599>(({ className, asChild = false, showOnHover = false, ...props }, ref) => { 600 const Comp = asChild ? Slot : "button" 601 602 return ( 603 <Comp 604 ref={ref} 605 data-sidebar="menu-action" 606 className={cn( 607 "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-hidden ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0", 608 // Increases the hit area of the button on mobile. 609 "after:absolute after:-inset-2 md:after:hidden", 610 "peer-data-[size=sm]/menu-button:top-1", 611 "peer-data-[size=default]/menu-button:top-1.5", 612 "peer-data-[size=lg]/menu-button:top-2.5", 613 "group-data-[collapsible=icon]:hidden", 614 showOnHover && 615 "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0", 616 className 617 )} 618 {...props} 619 /> 620 ) 621}) 622SidebarMenuAction.displayName = "SidebarMenuAction" 623 624const SidebarMenuBadge = React.forwardRef< 625 HTMLDivElement, 626 React.ComponentProps<"div"> 627>(({ className, ...props }, ref) => ( 628 <div 629 ref={ref} 630 data-sidebar="menu-badge" 631 className={cn( 632 "absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground select-none pointer-events-none", 633 "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground", 634 "peer-data-[size=sm]/menu-button:top-1", 635 "peer-data-[size=default]/menu-button:top-1.5", 636 "peer-data-[size=lg]/menu-button:top-2.5", 637 "group-data-[collapsible=icon]:hidden", 638 className 639 )} 640 {...props} 641 /> 642)) 643SidebarMenuBadge.displayName = "SidebarMenuBadge" 644 645const SidebarMenuSkeleton = React.forwardRef< 646 HTMLDivElement, 647 React.ComponentProps<"div"> & { 648 showIcon?: boolean 649 } 650>(({ className, showIcon = false, ...props }, ref) => { 651 // Random width between 50 to 90%. 652 const width = React.useMemo(() => { 653 return `${Math.floor(Math.random() * 40) + 50}%` 654 }, []) 655 656 return ( 657 <div 658 ref={ref} 659 data-sidebar="menu-skeleton" 660 className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)} 661 {...props} 662 > 663 {showIcon && ( 664 <Skeleton 665 className="size-4 rounded-md" 666 data-sidebar="menu-skeleton-icon" 667 /> 668 )} 669 <Skeleton 670 className="h-4 flex-1 max-w-(--skeleton-width)" 671 data-sidebar="menu-skeleton-text" 672 style={ 673 { 674 "--skeleton-width": width, 675 } as React.CSSProperties 676 } 677 /> 678 </div> 679 ) 680}) 681SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton" 682 683const SidebarMenuSub = React.forwardRef< 684 HTMLUListElement, 685 React.ComponentProps<"ul"> 686>(({ className, ...props }, ref) => ( 687 <ul 688 ref={ref} 689 data-sidebar="menu-sub" 690 className={cn( 691 "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5", 692 "group-data-[collapsible=icon]:hidden", 693 className 694 )} 695 {...props} 696 /> 697)) 698SidebarMenuSub.displayName = "SidebarMenuSub" 699 700const SidebarMenuSubItem = React.forwardRef< 701 HTMLLIElement, 702 React.ComponentProps<"li"> 703>(({ ...props }, ref) => <li ref={ref} {...props} />) 704SidebarMenuSubItem.displayName = "SidebarMenuSubItem" 705 706const SidebarMenuSubButton = React.forwardRef< 707 HTMLAnchorElement, 708 React.ComponentProps<"a"> & { 709 asChild?: boolean 710 size?: "sm" | "md" 711 isActive?: boolean 712 } 713>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => { 714 const Comp = asChild ? Slot : "a" 715 716 return ( 717 <Comp 718 ref={ref} 719 data-sidebar="menu-sub-button" 720 data-size={size} 721 data-active={isActive} 722 className={cn( 723 "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-hidden ring-sidebar-ring 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 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", 724 "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground", 725 size === "sm" && "text-xs", 726 size === "md" && "text-sm", 727 "group-data-[collapsible=icon]:hidden", 728 className 729 )} 730 {...props} 731 /> 732 ) 733}) 734SidebarMenuSubButton.displayName = "SidebarMenuSubButton" 735 736export { 737 Sidebar, 738 SidebarContent, 739 SidebarFooter, 740 SidebarGroup, 741 SidebarGroupAction, 742 SidebarGroupContent, 743 SidebarGroupLabel, 744 SidebarHeader, 745 SidebarInput, 746 SidebarInset, 747 SidebarMenu, 748 SidebarMenuAction, 749 SidebarMenuBadge, 750 SidebarMenuButton, 751 SidebarMenuItem, 752 SidebarMenuSkeleton, 753 SidebarMenuSub, 754 SidebarMenuSubButton, 755 SidebarMenuSubItem, 756 SidebarProvider, 757 SidebarRail, 758 SidebarSeparator, 759 SidebarTrigger, 760 useSidebar, 761}