Fork of atp.tools as a universal profile for people on the ATmosphere
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 [
128 state,
129 open,
130 setOpen,
131 isMobile,
132 openMobile,
133 setOpenMobile,
134 toggleSidebar,
135 ],
136 );
137
138 return (
139 <SidebarContext.Provider value={contextValue}>
140 <TooltipProvider delayDuration={0}>
141 <div
142 style={
143 {
144 "--sidebar-width": SIDEBAR_WIDTH,
145 "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
146 //...style,
147 } as React.CSSProperties
148 }
149 className={cn(
150 "group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar",
151 className,
152 )}
153 ref={ref}
154 {...props}
155 >
156 {children}
157 </div>
158 </TooltipProvider>
159 </SidebarContext.Provider>
160 );
161 },
162);
163SidebarProvider.displayName = "SidebarProvider";
164
165const Sidebar = React.forwardRef<
166 HTMLDivElement,
167 React.ComponentProps<"div"> & {
168 side?: "left" | "right";
169 variant?: "sidebar" | "floating" | "inset";
170 collapsible?: "offcanvas" | "icon" | "none";
171 }
172>(
173 (
174 {
175 side = "left",
176 variant = "sidebar",
177 collapsible = "offcanvas",
178 className,
179 children,
180 ...props
181 },
182 ref,
183 ) => {
184 const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
185
186 if (collapsible === "none") {
187 return (
188 <div
189 className={cn(
190 "flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground",
191 className,
192 )}
193 ref={ref}
194 {...props}
195 >
196 {children}
197 </div>
198 );
199 }
200
201 if (isMobile) {
202 return (
203 <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
204 <SheetContent
205 data-sidebar="sidebar"
206 data-mobile="true"
207 className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
208 style={
209 {
210 "--sidebar-width": SIDEBAR_WIDTH_MOBILE,
211 } as React.CSSProperties
212 }
213 side={side}
214 >
215 <div className="flex h-full w-full flex-col">{children}</div>
216 </SheetContent>
217 </Sheet>
218 );
219 }
220
221 return (
222 <div
223 ref={ref}
224 className="group peer hidden md:block text-sidebar-foreground"
225 data-state={state}
226 data-collapsible={state === "collapsed" ? collapsible : ""}
227 data-variant={variant}
228 data-side={side}
229 >
230 {/* This is what handles the sidebar gap on desktop */}
231 <div
232 className={cn(
233 "duration-200 relative h-svh w-[--sidebar-width] bg-transparent transition-[width] ease-linear",
234 "group-data-[collapsible=offcanvas]:w-0",
235 "group-data-[side=right]:rotate-180",
236 variant === "floating" || variant === "inset"
237 ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
238 : "group-data-[collapsible=icon]:w-[--sidebar-width-icon]",
239 )}
240 />
241 <div
242 className={cn(
243 "duration-200 fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] ease-linear md:flex",
244 side === "left"
245 ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
246 : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
247 // Adjust the padding for floating and inset variants.
248 variant === "floating" || variant === "inset"
249 ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
250 : "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
251 className,
252 )}
253 {...props}
254 >
255 <div
256 data-sidebar="sidebar"
257 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"
258 >
259 {children}
260 </div>
261 </div>
262 </div>
263 );
264 },
265);
266Sidebar.displayName = "Sidebar";
267
268const SidebarTrigger = React.forwardRef<
269 React.ElementRef<typeof Button>,
270 React.ComponentProps<typeof Button>
271>(({ className, onClick, ...props }, ref) => {
272 const { toggleSidebar } = useSidebar();
273
274 return (
275 <Button
276 ref={ref}
277 data-sidebar="trigger"
278 variant="ghost"
279 size="icon"
280 className={cn("h-10 w-10", className)}
281 onClick={(event) => {
282 onClick?.(event);
283 toggleSidebar();
284 }}
285 {...props}
286 >
287 <PanelLeft className="scale-125" />
288 <span className="sr-only">Toggle Sidebar</span>
289 </Button>
290 );
291});
292SidebarTrigger.displayName = "SidebarTrigger";
293
294const SidebarRail = React.forwardRef<
295 HTMLButtonElement,
296 React.ComponentProps<"button">
297>(({ className, ...props }, ref) => {
298 const { toggleSidebar } = useSidebar();
299
300 return (
301 <button
302 ref={ref}
303 data-sidebar="rail"
304 aria-label="Toggle Sidebar"
305 tabIndex={-1}
306 onClick={toggleSidebar}
307 title="Toggle Sidebar"
308 className={cn(
309 "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",
310 "[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize",
311 "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
312 "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar",
313 "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
314 "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
315 className,
316 )}
317 {...props}
318 />
319 );
320});
321SidebarRail.displayName = "SidebarRail";
322
323const SidebarInset = React.forwardRef<
324 HTMLDivElement,
325 React.ComponentProps<"main">
326>(({ className, ...props }, ref) => {
327 return (
328 <main
329 ref={ref as any}
330 className={cn(
331 "relative flex min-h-svh flex-1 flex-col bg-background",
332 "peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
333 className,
334 )}
335 {...props}
336 />
337 );
338});
339SidebarInset.displayName = "SidebarInset";
340
341const SidebarInput = React.forwardRef<
342 React.ElementRef<typeof Input>,
343 React.ComponentProps<typeof Input>
344>(({ className, ...props }, ref) => {
345 return (
346 <Input
347 ref={ref}
348 data-sidebar="input"
349 className={cn(
350 "h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring",
351 className,
352 )}
353 {...props}
354 />
355 );
356});
357SidebarInput.displayName = "SidebarInput";
358
359const SidebarHeader = React.forwardRef<
360 HTMLDivElement,
361 React.ComponentProps<"div">
362>(({ className, ...props }, ref) => {
363 return (
364 <div
365 ref={ref}
366 data-sidebar="header"
367 className={cn("flex flex-col gap-2 p-2", className)}
368 {...props}
369 />
370 );
371});
372SidebarHeader.displayName = "SidebarHeader";
373
374const SidebarFooter = React.forwardRef<
375 HTMLDivElement,
376 React.ComponentProps<"div">
377>(({ className, ...props }, ref) => {
378 return (
379 <div
380 ref={ref}
381 data-sidebar="footer"
382 className={cn("flex flex-col gap-2 p-2", className)}
383 {...props}
384 />
385 );
386});
387SidebarFooter.displayName = "SidebarFooter";
388
389const SidebarSeparator = React.forwardRef<
390 React.ElementRef<typeof Separator>,
391 React.ComponentProps<typeof Separator>
392>(({ className, ...props }, ref) => {
393 return (
394 <Separator
395 ref={ref}
396 data-sidebar="separator"
397 className={cn("mx-2 w-auto bg-sidebar-border", className)}
398 {...props}
399 />
400 );
401});
402SidebarSeparator.displayName = "SidebarSeparator";
403
404const SidebarContent = React.forwardRef<
405 HTMLDivElement,
406 React.ComponentProps<"div">
407>(({ className, ...props }, ref) => {
408 return (
409 <div
410 ref={ref}
411 data-sidebar="content"
412 className={cn(
413 "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
414 className,
415 )}
416 {...props}
417 />
418 );
419});
420SidebarContent.displayName = "SidebarContent";
421
422const SidebarGroup = React.forwardRef<
423 HTMLDivElement,
424 React.ComponentProps<"div">
425>(({ className, ...props }, ref) => {
426 return (
427 <div
428 ref={ref}
429 data-sidebar="group"
430 className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
431 {...props}
432 />
433 );
434});
435SidebarGroup.displayName = "SidebarGroup";
436
437const SidebarGroupLabel = React.forwardRef<
438 HTMLDivElement,
439 React.ComponentProps<"div"> & { asChild?: boolean }
440>(({ className, asChild = false, ...props }, ref) => {
441 const Comp = asChild ? Slot : "div";
442
443 return (
444 <Comp
445 ref={ref as any}
446 data-sidebar="group-label"
447 className={cn(
448 "duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
449 "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
450 className,
451 )}
452 {...props}
453 />
454 );
455});
456SidebarGroupLabel.displayName = "SidebarGroupLabel";
457
458const SidebarGroupAction = React.forwardRef<
459 HTMLButtonElement,
460 React.ComponentProps<"button"> & { asChild?: boolean }
461>(({ className, asChild = false, ...props }, ref) => {
462 const Comp = asChild ? Slot : "button";
463
464 return (
465 <Comp
466 ref={ref as any}
467 data-sidebar="group-action"
468 className={cn(
469 "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
470 // Increases the hit area of the button on mobile.
471 "after:absolute after:-inset-2 after:md:hidden",
472 "group-data-[collapsible=icon]:hidden",
473 className,
474 )}
475 {...props}
476 />
477 );
478});
479SidebarGroupAction.displayName = "SidebarGroupAction";
480
481const SidebarGroupContent = React.forwardRef<
482 HTMLDivElement,
483 React.ComponentProps<"div">
484>(({ className, ...props }, ref) => (
485 <div
486 ref={ref}
487 data-sidebar="group-content"
488 className={cn("w-full text-sm", className)}
489 {...props}
490 />
491));
492SidebarGroupContent.displayName = "SidebarGroupContent";
493
494const SidebarMenu = React.forwardRef<
495 HTMLUListElement,
496 React.ComponentProps<"ul">
497>(({ className, ...props }, ref) => (
498 <ul
499 ref={ref}
500 data-sidebar="menu"
501 className={cn("flex w-full min-w-0 flex-col gap-1", className)}
502 {...props}
503 />
504));
505SidebarMenu.displayName = "SidebarMenu";
506
507const SidebarMenuItem = React.forwardRef<
508 HTMLLIElement,
509 React.ComponentProps<"li">
510>(({ className, ...props }, ref) => (
511 <li
512 ref={ref}
513 data-sidebar="menu-item"
514 className={cn("group/menu-item relative", className)}
515 {...props}
516 />
517));
518SidebarMenuItem.displayName = "SidebarMenuItem";
519
520const sidebarMenuButtonVariants = cva(
521 "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none 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",
522 {
523 variants: {
524 variant: {
525 default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
526 outline:
527 "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))]",
528 },
529 size: {
530 default: "h-8 text-sm",
531 sm: "h-7 text-xs",
532 lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
533 },
534 },
535 defaultVariants: {
536 variant: "default",
537 size: "default",
538 },
539 },
540);
541
542const SidebarMenuButton = React.forwardRef<
543 HTMLButtonElement,
544 React.ComponentProps<"button"> & {
545 asChild?: boolean;
546 isActive?: boolean;
547 tooltip?: string | React.ComponentProps<typeof TooltipContent>;
548 } & VariantProps<typeof sidebarMenuButtonVariants>
549>(
550 (
551 {
552 asChild = false,
553 isActive = false,
554 variant = "default",
555 size = "default",
556 tooltip,
557 className,
558 ...props
559 },
560 ref,
561 ) => {
562 const Comp = asChild ? Slot : "button";
563 const { isMobile, state } = useSidebar();
564
565 const button = (
566 <Comp
567 ref={ref as any}
568 data-sidebar="menu-button"
569 data-size={size}
570 data-active={isActive}
571 className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
572 {...props}
573 />
574 );
575
576 if (!tooltip) {
577 return button;
578 }
579
580 if (typeof tooltip === "string") {
581 tooltip = {
582 children: tooltip,
583 };
584 }
585
586 return (
587 <Tooltip>
588 <TooltipTrigger asChild>{button}</TooltipTrigger>
589 <TooltipContent
590 side="right"
591 align="center"
592 hidden={state !== "collapsed" || isMobile}
593 {...tooltip}
594 />
595 </Tooltip>
596 );
597 },
598);
599SidebarMenuButton.displayName = "SidebarMenuButton";
600
601const SidebarMenuAction = React.forwardRef<
602 HTMLButtonElement,
603 React.ComponentProps<"button"> & {
604 asChild?: boolean;
605 showOnHover?: boolean;
606 }
607>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
608 const Comp = asChild ? Slot : "button";
609
610 return (
611 <Comp
612 ref={ref as any}
613 data-sidebar="menu-action"
614 className={cn(
615 "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none 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",
616 // Increases the hit area of the button on mobile.
617 "after:absolute after:-inset-2 after:md:hidden",
618 "peer-data-[size=sm]/menu-button:top-1",
619 "peer-data-[size=default]/menu-button:top-1.5",
620 "peer-data-[size=lg]/menu-button:top-2.5",
621 "group-data-[collapsible=icon]:hidden",
622 showOnHover &&
623 "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",
624 className,
625 )}
626 {...props}
627 />
628 );
629});
630SidebarMenuAction.displayName = "SidebarMenuAction";
631
632const SidebarMenuBadge = React.forwardRef<
633 HTMLDivElement,
634 React.ComponentProps<"div">
635>(({ className, ...props }, ref) => (
636 <div
637 ref={ref}
638 data-sidebar="menu-badge"
639 className={cn(
640 "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",
641 "peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
642 "peer-data-[size=sm]/menu-button:top-1",
643 "peer-data-[size=default]/menu-button:top-1.5",
644 "peer-data-[size=lg]/menu-button:top-2.5",
645 "group-data-[collapsible=icon]:hidden",
646 className,
647 )}
648 {...props}
649 />
650));
651SidebarMenuBadge.displayName = "SidebarMenuBadge";
652
653const SidebarMenuSkeleton = React.forwardRef<
654 HTMLDivElement,
655 React.ComponentProps<"div"> & {
656 showIcon?: boolean;
657 }
658>(({ className, showIcon = false, ...props }, ref) => {
659 // Random width between 50 to 90%.
660 const width = React.useMemo(() => {
661 return `${Math.floor(Math.random() * 40) + 50}%`;
662 }, []);
663
664 return (
665 <div
666 ref={ref}
667 data-sidebar="menu-skeleton"
668 className={cn("rounded-md h-8 flex gap-2 px-2 items-center", className)}
669 {...props}
670 >
671 {showIcon && (
672 <Skeleton
673 className="size-4 rounded-md"
674 data-sidebar="menu-skeleton-icon"
675 />
676 )}
677 <Skeleton
678 className="h-4 flex-1 max-w-[--skeleton-width]"
679 data-sidebar="menu-skeleton-text"
680 style={
681 {
682 "--skeleton-width": width,
683 } as React.CSSProperties
684 }
685 />
686 </div>
687 );
688});
689SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton";
690
691const SidebarMenuSub = React.forwardRef<
692 HTMLUListElement,
693 React.ComponentProps<"ul">
694>(({ className, ...props }, ref) => (
695 <ul
696 ref={ref}
697 data-sidebar="menu-sub"
698 className={cn(
699 "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",
700 "group-data-[collapsible=icon]:hidden",
701 className,
702 )}
703 {...props}
704 />
705));
706SidebarMenuSub.displayName = "SidebarMenuSub";
707
708const SidebarMenuSubItem = React.forwardRef<
709 HTMLLIElement,
710 React.ComponentProps<"li">
711>(({ ...props }, ref) => <li ref={ref} {...props} />);
712SidebarMenuSubItem.displayName = "SidebarMenuSubItem";
713
714const SidebarMenuSubButton = React.forwardRef<
715 HTMLAnchorElement,
716 React.ComponentProps<"a"> & {
717 asChild?: boolean;
718 size?: "sm" | "md";
719 isActive?: boolean;
720 }
721>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => {
722 const Comp = asChild ? Slot : "a";
723
724 return (
725 <Comp
726 ref={ref as any}
727 data-sidebar="menu-sub-button"
728 data-size={size}
729 data-active={isActive}
730 className={cn(
731 "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none 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",
732 "data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
733 size === "sm" && "text-xs",
734 size === "md" && "text-sm",
735 "group-data-[collapsible=icon]:hidden",
736 className,
737 )}
738 {...props}
739 />
740 );
741});
742SidebarMenuSubButton.displayName = "SidebarMenuSubButton";
743
744export {
745 Sidebar,
746 SidebarContent,
747 SidebarFooter,
748 SidebarGroup,
749 SidebarGroupAction,
750 SidebarGroupContent,
751 SidebarGroupLabel,
752 SidebarHeader,
753 SidebarInput,
754 SidebarInset,
755 SidebarMenu,
756 SidebarMenuAction,
757 SidebarMenuBadge,
758 SidebarMenuButton,
759 SidebarMenuItem,
760 SidebarMenuSkeleton,
761 SidebarMenuSub,
762 SidebarMenuSubButton,
763 SidebarMenuSubItem,
764 SidebarProvider,
765 SidebarRail,
766 SidebarSeparator,
767 SidebarTrigger,
768 useSidebar,
769};