{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "notra-sidebar",
  "title": "Sidebar",
  "description": "The shadcn sidebar on Base UI with a fading icon rail, a resizable rail, collapsible sub-rows and gliding Notra tooltips.",
  "dependencies": [
    "@base-ui/react",
    "@hugeicons/core-free-icons",
    "@hugeicons/react",
    "class-variance-authority",
    "cn"
  ],
  "registryDependencies": [
    "https://ui.usenotra.com/r/notra-button.json",
    "https://ui.usenotra.com/r/notra-tooltip.json",
    "input",
    "separator",
    "sheet",
    "skeleton"
  ],
  "files": [
    {
      "path": "registry/notra/sidebar/components/sidebar.tsx",
      "content": "\"use client\";\n\nimport { Collapsible as CollapsiblePrimitive } from \"@base-ui/react/collapsible\";\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport {\n  SidebarLeft01Icon,\n  SidebarRight01Icon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"cn\";\nimport * as React from \"react\";\n\nimport { Input } from \"@/components/ui/input\";\nimport { Separator } from \"@/components/ui/separator\";\nimport {\n  Sheet,\n  SheetContent,\n  SheetDescription,\n  SheetHeader,\n  SheetTitle,\n} from \"@/components/ui/sheet\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\n\nimport { Button } from \"../../button/components/button\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"../../tooltip/components/tooltip\";\n\nconst SIDEBAR_COOKIE_NAME = \"sidebar_state\";\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;\nconst SIDEBAR_WIDTH = 256;\nconst SIDEBAR_MIN_WIDTH = 240;\nconst SIDEBAR_MAX_WIDTH = 400;\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\";\nconst SIDEBAR_MOBILE_BREAKPOINT = 768;\nconst SIDEBAR_WIDTH_ICON = \"3rem\";\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\";\nconst SIDEBAR_RESIZE_STEP = 8;\n// A drag shorter than this is a click, past this it collapses or reopens.\nconst SIDEBAR_DRAG_SLOP = 4;\nconst SIDEBAR_SNAP_DISTANCE = 40;\nconst SIDEBAR_TRANSITION =\n  \"duration-(--sidebar-duration) ease-(--sidebar-ease) in-data-[resizing=true]:transition-none motion-reduce:transition-none\";\n\ntype SidebarContextProps = {\n  state: \"expanded\" | \"collapsed\";\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  openMobile: boolean;\n  setOpenMobile: (open: boolean) => void;\n  isMobile: boolean;\n  toggleSidebar: () => void;\n  width: number;\n  setWidth: (width: number) => void;\n  defaultWidth: number;\n  minWidth: number;\n  maxWidth: number;\n  resizing: boolean;\n  setResizing: (resizing: boolean) => void;\n  contentRef: React.RefObject<HTMLDivElement | null>;\n};\n\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null);\n\nfunction useSidebar() {\n  const context = React.use(SidebarContext);\n  if (!context) {\n    throw new Error(\"useSidebar must be used within a SidebarProvider.\");\n  }\n\n  return context;\n}\n\nfunction SidebarProvider({\n  defaultOpen = true,\n  open: openProp,\n  onOpenChange,\n  defaultWidth = SIDEBAR_WIDTH,\n  width: widthProp,\n  onWidthChange,\n  minWidth = SIDEBAR_MIN_WIDTH,\n  maxWidth = SIDEBAR_MAX_WIDTH,\n  keyboardShortcut = SIDEBAR_KEYBOARD_SHORTCUT,\n  mobileBreakpoint = SIDEBAR_MOBILE_BREAKPOINT,\n  className,\n  style,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Expanded width in px. Drag the rail or use the arrow keys to resize. */\n  defaultWidth?: number;\n  width?: number;\n  onWidthChange?: (width: number) => void;\n  minWidth?: number;\n  maxWidth?: number;\n  /** Toggles the sidebar with ⌘ or Ctrl. Pass `null` to disable. */\n  keyboardShortcut?: string | null;\n  /**\n   * Below this width in px the sidebar opens as a sheet. It is measured on the\n   * provider rather than the viewport, so an embedded layout switches too.\n   */\n  mobileBreakpoint?: number;\n}) {\n  const [isMobile, setIsMobile] = React.useState(false);\n  const wrapperRef = React.useRef<HTMLDivElement>(null);\n  React.useLayoutEffect(() => {\n    const wrapper = wrapperRef.current;\n    if (!wrapper) {\n      return;\n    }\n    const observer = new ResizeObserver(([entry]) => {\n      if (entry) {\n        setIsMobile(entry.contentRect.width < mobileBreakpoint);\n      }\n    });\n    observer.observe(wrapper);\n    return () => observer.disconnect();\n  }, [mobileBreakpoint]);\n  const [openMobile, setOpenMobile] = React.useState(false);\n  const [resizing, setResizing] = React.useState(false);\n  const contentRef = React.useRef<HTMLDivElement | null>(null);\n\n  const [openState, setOpenState] = React.useState(defaultOpen);\n  const open = openProp ?? openState;\n  const setOpen = React.useCallback(\n    (value: boolean) => {\n      if (onOpenChange) {\n        onOpenChange(value);\n      } else {\n        setOpenState(value);\n      }\n      document.cookie = `${SIDEBAR_COOKIE_NAME}=${value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;\n    },\n    [onOpenChange]\n  );\n\n  const [widthState, setWidthState] = React.useState(defaultWidth);\n  const width = widthProp ?? widthState;\n  const setWidth = React.useCallback(\n    (value: number) => {\n      const next = Math.min(maxWidth, Math.max(minWidth, Math.round(value)));\n      if (onWidthChange) {\n        onWidthChange(next);\n      } else {\n        setWidthState(next);\n      }\n    },\n    [maxWidth, minWidth, onWidthChange]\n  );\n\n  const toggleSidebar = React.useCallback(() => {\n    if (isMobile) {\n      setOpenMobile((value) => !value);\n    } else {\n      setOpen(!open);\n    }\n  }, [isMobile, open, setOpen]);\n\n  const onShortcut = React.useEffectEvent(() => toggleSidebar());\n\n  React.useEffect(() => {\n    if (!keyboardShortcut) {\n      return;\n    }\n\n    const handleKeyDown = (event: KeyboardEvent) => {\n      if (\n        event.key === keyboardShortcut &&\n        (event.metaKey || event.ctrlKey) &&\n        !event.altKey &&\n        !event.shiftKey\n      ) {\n        event.preventDefault();\n        onShortcut();\n      }\n    };\n\n    window.addEventListener(\"keydown\", handleKeyDown);\n    return () => window.removeEventListener(\"keydown\", handleKeyDown);\n  }, [keyboardShortcut]);\n\n  const state = open ? \"expanded\" : \"collapsed\";\n\n  const contextValue = React.useMemo<SidebarContextProps>(\n    () => ({\n      state,\n      open,\n      setOpen,\n      isMobile,\n      openMobile,\n      setOpenMobile,\n      toggleSidebar,\n      width,\n      setWidth,\n      defaultWidth,\n      minWidth,\n      maxWidth,\n      resizing,\n      setResizing,\n      contentRef,\n    }),\n    [\n      state,\n      open,\n      setOpen,\n      isMobile,\n      openMobile,\n      toggleSidebar,\n      width,\n      setWidth,\n      defaultWidth,\n      minWidth,\n      maxWidth,\n      resizing,\n    ]\n  );\n\n  return (\n    <SidebarContext value={contextValue}>\n      <div\n        ref={wrapperRef}\n        data-slot=\"sidebar-wrapper\"\n        data-resizing={resizing || undefined}\n        style={\n          {\n            \"--sidebar-width\": `${width}px`,\n            \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n            \"--sidebar-rail-content\": \"calc(var(--sidebar-width-icon) - 1rem)\",\n            \"--sidebar-duration\": \"300ms\",\n            \"--sidebar-ease\": \"cubic-bezier(0.22, 1, 0.36, 1)\",\n            ...style,\n          } as React.CSSProperties\n        }\n        className={cn(\n          \"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full min-w-0 data-resizing:cursor-col-resize data-resizing:select-none\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n      </div>\n    </SidebarContext>\n  );\n}\n\nfunction Sidebar({\n  side = \"left\",\n  variant = \"sidebar\",\n  collapsible = \"offcanvas\",\n  mobileTitle = \"Sidebar\",\n  mobileDescription = \"Displays the mobile sidebar.\",\n  className,\n  children,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  side?: \"left\" | \"right\";\n  variant?: \"sidebar\" | \"floating\" | \"inset\";\n  collapsible?: \"offcanvas\" | \"icon\" | \"none\";\n  mobileTitle?: string;\n  mobileDescription?: string;\n}) {\n  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();\n\n  if (collapsible === \"none\") {\n    return (\n      <div\n        data-slot=\"sidebar\"\n        className={cn(\n          \"bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col\",\n          className\n        )}\n        {...props}\n      >\n        <TooltipProvider>{children}</TooltipProvider>\n      </div>\n    );\n  }\n\n  if (isMobile) {\n    return (\n      <Sheet open={openMobile} onOpenChange={setOpenMobile}>\n        <SheetContent\n          data-sidebar=\"sidebar\"\n          data-slot=\"sidebar\"\n          data-mobile=\"true\"\n          side={side}\n          showCloseButton={false}\n          style={\n            { \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE } as React.CSSProperties\n          }\n          className={cn(\n            \"bg-sidebar text-sidebar-foreground border-sidebar-border w-(--sidebar-width) gap-0 p-0\",\n            className\n          )}\n        >\n          <SheetHeader className=\"sr-only\">\n            <SheetTitle>{mobileTitle}</SheetTitle>\n            <SheetDescription>{mobileDescription}</SheetDescription>\n          </SheetHeader>\n          <div className=\"flex h-full w-full flex-col\">\n            <TooltipProvider>{children}</TooltipProvider>\n          </div>\n        </SheetContent>\n      </Sheet>\n    );\n  }\n\n  return (\n    <div\n      className=\"group peer text-sidebar-foreground block\"\n      data-state={state}\n      data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n      data-variant={variant}\n      data-side={side}\n      data-slot=\"sidebar\"\n    >\n      <div\n        data-slot=\"sidebar-gap\"\n        className={cn(\n          \"relative w-(--sidebar-width) bg-transparent transition-[width]\",\n          SIDEBAR_TRANSITION,\n          \"group-data-[collapsible=offcanvas]:w-0 group-data-[side=right]:rotate-180\",\n          variant === \"floating\" || variant === \"inset\"\n            ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]\"\n            : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\"\n        )}\n      />\n      <div\n        data-slot=\"sidebar-container\"\n        className={cn(\n          \"fixed inset-y-0 z-10 flex h-svh w-(--sidebar-width) transition-[left,right,width]\",\n          SIDEBAR_TRANSITION,\n          side === \"left\"\n            ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n            : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n          variant === \"floating\" || variant === \"inset\"\n            ? \"p-2 [--sidebar-rail-content:calc(var(--sidebar-width-icon)-1rem+2px)] group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]\"\n            : \"border-sidebar-border group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l\",\n          className\n        )}\n        {...props}\n      >\n        <div\n          data-sidebar=\"sidebar\"\n          data-slot=\"sidebar-inner\"\n          className=\"bg-sidebar group-data-[variant=floating]:ring-sidebar-border flex size-full flex-col group-data-[variant=floating]:rounded-xl group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1\"\n        >\n          <TooltipProvider>{children}</TooltipProvider>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction SidebarTrigger({\n  className,\n  onClick,\n  ...props\n}: React.ComponentProps<typeof Button>) {\n  const { isMobile, open, openMobile, toggleSidebar } = useSidebar();\n  const isOpen = isMobile ? openMobile : open;\n\n  return (\n    <Button\n      data-sidebar=\"trigger\"\n      data-slot=\"sidebar-trigger\"\n      variant=\"ghost\"\n      size=\"icon-sm\"\n      aria-expanded={isOpen}\n      aria-label={isOpen ? \"Hide sidebar\" : \"Show sidebar\"}\n      className={cn(\n        \"text-foreground/70 hover:text-foreground aria-expanded:text-foreground/70 aria-expanded:hover:text-foreground bg-transparent! hover:bg-transparent! active:bg-transparent! aria-expanded:bg-transparent!\",\n        className\n      )}\n      onClick={(event) => {\n        onClick?.(event);\n        toggleSidebar();\n      }}\n      {...props}\n    >\n      <HugeiconsIcon\n        icon={isOpen ? SidebarLeft01Icon : SidebarRight01Icon}\n        strokeWidth={1.8}\n      />\n    </Button>\n  );\n}\n\n/**\n * The edge of the sidebar. Drag it to resize, drag it far enough past the\n * minimum to collapse, and double-click to restore the default width. Arrow\n * keys resize while it has focus. Pass `resizable={false}` for a plain toggle.\n */\nfunction SidebarRail({\n  resizable = true,\n  className,\n  ...props\n}: React.ComponentProps<\"button\"> & { resizable?: boolean }) {\n  const {\n    state,\n    setOpen,\n    toggleSidebar,\n    width,\n    setWidth,\n    defaultWidth,\n    minWidth,\n    maxWidth,\n    setResizing,\n  } = useSidebar();\n  const dragRef = React.useRef<{ startX: number; startWidth: number } | null>(\n    null\n  );\n  const collapsed = state === \"collapsed\";\n\n  if (!resizable) {\n    return (\n      <button\n        data-sidebar=\"rail\"\n        data-slot=\"sidebar-rail\"\n        aria-label=\"Toggle sidebar\"\n        tabIndex={-1}\n        onClick={toggleSidebar}\n        title=\"Toggle sidebar\"\n        className={cn(\n          \"hover:after:bg-sidebar-border absolute inset-y-0 z-20 flex w-4 -translate-x-1/2 cursor-pointer transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px]\",\n          className\n        )}\n        {...props}\n      />\n    );\n  }\n\n  const endDrag = (event: React.PointerEvent<HTMLButtonElement>) => {\n    if (!dragRef.current) {\n      return;\n    }\n    dragRef.current = null;\n    setResizing(false);\n    if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n      event.currentTarget.releasePointerCapture(event.pointerId);\n    }\n  };\n\n  return (\n    <button\n      type=\"button\"\n      role=\"separator\"\n      data-sidebar=\"rail\"\n      data-slot=\"sidebar-rail\"\n      aria-label=\"Resize sidebar\"\n      aria-orientation=\"vertical\"\n      aria-valuemin={0}\n      aria-valuemax={maxWidth}\n      aria-valuenow={collapsed ? 0 : width}\n      aria-valuetext={collapsed ? \"Collapsed\" : `${width} pixels`}\n      title=\"Drag to resize, double-click to reset\"\n      className={cn(\n        \"hover:after:bg-sidebar-border focus-visible:after:bg-sidebar-ring in-data-resizing:after:bg-sidebar-ring absolute inset-y-0 z-20 flex w-4 -translate-x-1/2 cursor-col-resize touch-none outline-none group-data-[collapsible=icon]:cursor-e-resize group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-2 after:start-1/2 after:w-[2px] after:rounded-full after:transition-colors\",\n        \"group-data-[collapsible=offcanvas]:hidden\",\n        className\n      )}\n      onPointerDown={(event) => {\n        if (event.button !== 0) {\n          return;\n        }\n        event.preventDefault();\n        dragRef.current = { startX: event.clientX, startWidth: width };\n        event.currentTarget.setPointerCapture(event.pointerId);\n      }}\n      onPointerMove={(event) => {\n        const drag = dragRef.current;\n        if (!drag) {\n          return;\n        }\n        // Dragging away from the sidebar's edge always grows it.\n        const delta = (event.clientX - drag.startX) * railDirection(event);\n        if (Math.abs(delta) < SIDEBAR_DRAG_SLOP) {\n          return;\n        }\n        if (collapsed) {\n          if (delta >= SIDEBAR_SNAP_DISTANCE) {\n            endDrag(event);\n            setOpen(true);\n          }\n          return;\n        }\n        setResizing(true);\n        const dragged = drag.startWidth + delta;\n        setWidth(dragged);\n        if (dragged < minWidth - SIDEBAR_SNAP_DISTANCE) {\n          endDrag(event);\n          setOpen(false);\n        }\n      }}\n      onPointerUp={endDrag}\n      onPointerCancel={endDrag}\n      onLostPointerCapture={endDrag}\n      onClick={(event) => {\n        // Enter or Space on the focused rail toggles it.\n        if (event.detail === 0) {\n          toggleSidebar();\n        }\n      }}\n      onDoubleClick={() => {\n        setOpen(true);\n        setWidth(defaultWidth);\n      }}\n      onKeyDown={(event) => {\n        let next: number | undefined;\n        const grow = railDirection(event) === 1 ? \"ArrowRight\" : \"ArrowLeft\";\n        const shrink = grow === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n        if (event.key === shrink) {\n          if (collapsed) {\n            return;\n          }\n          if (width <= minWidth) {\n            event.preventDefault();\n            setOpen(false);\n            return;\n          }\n          next = width - SIDEBAR_RESIZE_STEP;\n        } else if (event.key === grow) {\n          next = collapsed ? width : width + SIDEBAR_RESIZE_STEP;\n        } else if (event.key === \"Home\") {\n          next = minWidth;\n        } else if (event.key === \"End\") {\n          next = maxWidth;\n        }\n        if (next === undefined) {\n          return;\n        }\n        event.preventDefault();\n        setOpen(true);\n        setWidth(next);\n      }}\n      {...props}\n    />\n  );\n}\n\n/** 1 when the sidebar grows to the right (left side), -1 for a right-side one. */\nfunction railDirection(event: React.SyntheticEvent<HTMLElement>) {\n  return event.currentTarget\n    .closest(\"[data-side]\")\n    ?.getAttribute(\"data-side\") === \"right\"\n    ? -1\n    : 1;\n}\n\nfunction SidebarInset({ className, ...props }: React.ComponentProps<\"main\">) {\n  return (\n    <main\n      data-slot=\"sidebar-inset\"\n      className={cn(\n        \"bg-background relative flex w-full min-w-0 flex-1 flex-col transition-[margin]\",\n        SIDEBAR_TRANSITION,\n        // The peer only renders on desktop; the mobile sidebar is a sheet.\n        \"peer-data-[variant=inset]:border-sidebar-border peer-data-[variant=inset]:m-2 peer-data-[variant=inset]:ml-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:border peer-data-[variant=inset]:shadow-[0_1px_2px_rgba(0,0,0,0.04)] peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2 dark:peer-data-[variant=inset]:shadow-none\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarInput({\n  className,\n  ...props\n}: React.ComponentProps<typeof Input>) {\n  return (\n    <Input\n      data-slot=\"sidebar-input\"\n      data-sidebar=\"input\"\n      className={cn(\"bg-background h-8 w-full shadow-none\", className)}\n      {...props}\n    />\n  );\n}\n\n/** Wheel events over the header and footer scroll the content between them. */\nfunction useProxyWheel(onWheel?: React.WheelEventHandler<HTMLDivElement>) {\n  const { contentRef } = useSidebar();\n\n  return (event: React.WheelEvent<HTMLDivElement>) => {\n    onWheel?.(event);\n    const content = contentRef.current;\n    if (!content || event.deltaY === 0 || event.defaultPrevented) {\n      return;\n    }\n    content.scrollTop += event.deltaY;\n  };\n}\n\nfunction SidebarHeader({\n  className,\n  onWheel,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-header\"\n      data-sidebar=\"header\"\n      className={cn(\"flex flex-col gap-2 overflow-x-hidden p-2\", className)}\n      onWheel={useProxyWheel(onWheel)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarFooter({\n  className,\n  onWheel,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-footer\"\n      data-sidebar=\"footer\"\n      className={cn(\"flex flex-col gap-2 overflow-x-hidden p-2\", className)}\n      onWheel={useProxyWheel(onWheel)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarSeparator({\n  className,\n  ...props\n}: React.ComponentProps<typeof Separator>) {\n  return (\n    <Separator\n      data-slot=\"sidebar-separator\"\n      data-sidebar=\"separator\"\n      className={cn(\"bg-sidebar-border mx-2 w-auto\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarContent({\n  className,\n  ref,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  const { contentRef } = useSidebar();\n  const composedRef = React.useCallback(\n    (node: HTMLDivElement | null) => {\n      contentRef.current = node;\n      if (typeof ref === \"function\") {\n        ref(node);\n      } else if (ref) {\n        ref.current = node;\n      }\n    },\n    [contentRef, ref]\n  );\n\n  return (\n    <div\n      ref={composedRef}\n      data-slot=\"sidebar-content\"\n      data-sidebar=\"content\"\n      className={cn(\n        \"flex min-h-0 flex-1 flex-col gap-0 overflow-x-hidden overflow-y-auto overscroll-contain [scrollbar-width:none] group-data-[collapsible=icon]:overflow-hidden [&::-webkit-scrollbar]:hidden\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-group\"\n      data-sidebar=\"group\"\n      className={cn(\"relative flex w-full min-w-0 flex-col p-2\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarGroupLabel({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"div\"> & React.ComponentProps<\"div\">) {\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(\n      {\n        className: cn(\n          \"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center overflow-hidden rounded-md px-2 text-xs font-medium whitespace-nowrap outline-hidden transition-[margin,opacity] group-data-[collapsible=icon]:pointer-events-none group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n          SIDEBAR_TRANSITION,\n          className\n        ),\n      },\n      props\n    ),\n    render,\n    state: { slot: \"sidebar-group-label\", sidebar: \"group-label\" },\n  });\n}\n\nfunction SidebarGroupAction({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<\"button\"> & React.ComponentProps<\"button\">) {\n  return useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(\n      {\n        className: cn(\n          \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0\",\n          className\n        ),\n      },\n      props\n    ),\n    render,\n    state: { slot: \"sidebar-group-action\", sidebar: \"group-action\" },\n  });\n}\n\nfunction SidebarGroupContent({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-group-content\"\n      data-sidebar=\"group-content\"\n      className={cn(\"w-full text-sm\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenu({ className, ...props }: React.ComponentProps<\"ul\">) {\n  return (\n    <ul\n      data-slot=\"sidebar-menu\"\n      data-sidebar=\"menu\"\n      className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuItem({ className, ...props }: React.ComponentProps<\"li\">) {\n  return (\n    <li\n      data-slot=\"sidebar-menu-item\"\n      data-sidebar=\"menu-item\"\n      className={cn(\"group/menu-item relative\", className)}\n      {...props}\n    />\n  );\n}\n\n/**\n * Collapsing to the icon rail keeps the button's width animation intact: the\n * row clips to the rail and everything after the leading icon fades out.\n */\nconst sidebarMenuButtonVariants = cva(\n  \"peer/menu-button group/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent data-active:bg-sidebar-accent data-active:text-foreground data-popup-open:bg-sidebar-accent flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden group-has-data-[sidebar=menu-action]/menu-item:pr-8 focus-visible:ring-2 focus-visible:ring-inset disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-active:font-medium [&_svg]:size-4 [&_svg]:shrink-0 [&>*:not(:first-child)]:transition-opacity [&>*:not(:first-child)]:duration-150 [&>*:not(:first-child)]:ease-(--sidebar-ease) group-data-[collapsible=icon]:[&>*:not(:first-child)]:pointer-events-none group-data-[collapsible=icon]:[&>*:not(:first-child)]:opacity-0 [&>span:last-child]:truncate\",\n  {\n    variants: {\n      variant: {\n        default: \"\",\n        outline:\n          \"bg-background hover:bg-sidebar-accent shadow-[0_0_0_1px_var(--sidebar-border)] hover:shadow-[0_0_0_1px_var(--sidebar-accent)]\",\n      },\n      size: {\n        default: \"h-8\",\n        sm: \"h-7 text-xs\",\n        lg: \"h-12 transition-[padding] group-data-[collapsible=icon]:p-0! group-data-[collapsible=icon]:pl-[calc((var(--sidebar-rail-content)-var(--sidebar-lg-icon,2rem))/2)]!\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nfunction SidebarMenuButton({\n  render,\n  isActive = false,\n  variant = \"default\",\n  size = \"default\",\n  tooltip,\n  className,\n  ...props\n}: useRender.ComponentProps<\"button\"> &\n  React.ComponentProps<\"button\"> & {\n    isActive?: boolean;\n    /** Shown beside the icon rail while the sidebar is collapsed. */\n    tooltip?: string | React.ComponentProps<typeof TooltipContent>;\n  } & VariantProps<typeof sidebarMenuButtonVariants>) {\n  const { isMobile, state } = useSidebar();\n  // The mobile sheet shows labels, and focusing its first row must not open\n  // an empty tooltip.\n  const withTooltip = Boolean(tooltip) && !isMobile;\n  const button = useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(\n      {\n        className: cn(sidebarMenuButtonVariants({ variant, size }), className),\n      },\n      props\n    ),\n    render: withTooltip ? <TooltipTrigger render={render} /> : render,\n    state: {\n      slot: \"sidebar-menu-button\",\n      sidebar: \"menu-button\",\n      size,\n      active: isActive,\n    },\n  });\n\n  if (!(withTooltip && tooltip)) {\n    return button;\n  }\n\n  const tooltipProps =\n    typeof tooltip === \"string\" ? { children: tooltip } : tooltip;\n\n  return (\n    <Tooltip>\n      {button}\n      <TooltipContent\n        side=\"right\"\n        align=\"center\"\n        sideOffset={8}\n        hidden={state !== \"collapsed\"}\n        {...tooltipProps}\n      />\n    </Tooltip>\n  );\n}\n\nfunction SidebarMenuAction({\n  className,\n  render,\n  showOnHover = false,\n  ...props\n}: useRender.ComponentProps<\"button\"> &\n  React.ComponentProps<\"button\"> & {\n    showOnHover?: boolean;\n  }) {\n  return useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(\n      {\n        className: cn(\n          \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-3.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0\",\n          showOnHover &&\n            \"peer-data-active/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-popup-open:opacity-100 md:opacity-0\",\n          className\n        ),\n      },\n      props\n    ),\n    render,\n    state: { slot: \"sidebar-menu-action\", sidebar: \"menu-action\" },\n  });\n}\n\nfunction SidebarMenuBadge({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"sidebar-menu-badge\"\n      data-sidebar=\"menu-badge\"\n      className={cn(\n        \"text-muted-foreground peer-hover/menu-button:text-sidebar-accent-foreground pointer-events-none absolute right-1.5 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none group-data-[collapsible=icon]:hidden peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-3.5 peer-data-[size=sm]/menu-button:top-1\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuSkeleton({\n  className,\n  showIcon = false,\n  width = \"70%\",\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  showIcon?: boolean;\n  width?: string;\n}) {\n  return (\n    <div\n      data-slot=\"sidebar-menu-skeleton\"\n      data-sidebar=\"menu-skeleton\"\n      className={cn(\"flex h-8 items-center gap-2 rounded-lg px-2\", className)}\n      {...props}\n    >\n      {showIcon && (\n        <Skeleton\n          className=\"size-4 rounded-md\"\n          data-sidebar=\"menu-skeleton-icon\"\n        />\n      )}\n      <Skeleton\n        className=\"h-4 max-w-(--skeleton-width) flex-1\"\n        data-sidebar=\"menu-skeleton-text\"\n        style={{ \"--skeleton-width\": width } as React.CSSProperties}\n      />\n    </div>\n  );\n}\n\n/** A menu item that can reveal sub-rows, built on the Base UI Collapsible. */\nfunction SidebarMenuCollapsible({\n  className,\n  ...props\n}: CollapsiblePrimitive.Root.Props) {\n  return (\n    <CollapsiblePrimitive.Root\n      data-slot=\"sidebar-menu-item\"\n      data-sidebar=\"menu-item\"\n      render={<li />}\n      className={cn(\"group/menu-item relative\", className)}\n      {...props}\n    />\n  );\n}\n\n/**\n * Animates the sub-rows open and closed, and folds them away while the sidebar\n * is collapsed to the icon rail so the rows never show beside an icon.\n */\nfunction SidebarMenuSubPanel({\n  className,\n  children,\n  ...props\n}: CollapsiblePrimitive.Panel.Props) {\n  return (\n    <CollapsiblePrimitive.Panel\n      data-slot=\"sidebar-menu-sub-panel\"\n      className={cn(\n        \"h-(--collapsible-panel-height) overflow-hidden transition-[height,opacity] duration-200 ease-(--sidebar-ease) data-ending-style:h-0 data-ending-style:opacity-0 data-starting-style:h-0 data-starting-style:opacity-0 motion-reduce:transition-none\",\n        className\n      )}\n      {...props}\n    >\n      <div\n        className={cn(\n          \"grid grid-rows-[1fr] transition-[grid-template-rows,opacity] group-data-[collapsible=icon]:grid-rows-[0fr] group-data-[collapsible=icon]:opacity-0\",\n          SIDEBAR_TRANSITION\n        )}\n      >\n        <div className=\"min-h-0 overflow-hidden\">\n          {children as React.ReactNode}\n        </div>\n      </div>\n    </CollapsiblePrimitive.Panel>\n  );\n}\n\nfunction SidebarMenuSub({ className, ...props }: React.ComponentProps<\"ul\">) {\n  return (\n    <ul\n      data-slot=\"sidebar-menu-sub\"\n      data-sidebar=\"menu-sub\"\n      className={cn(\n        \"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\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuSubItem({\n  className,\n  ...props\n}: React.ComponentProps<\"li\">) {\n  return (\n    <li\n      data-slot=\"sidebar-menu-sub-item\"\n      data-sidebar=\"menu-sub-item\"\n      className={cn(\"group/menu-sub-item relative\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction SidebarMenuSubButton({\n  render,\n  size = \"md\",\n  isActive = false,\n  className,\n  ...props\n}: useRender.ComponentProps<\"a\"> &\n  React.ComponentProps<\"a\"> & {\n    size?: \"sm\" | \"md\";\n    isActive?: boolean;\n  }) {\n  return useRender({\n    defaultTagName: \"a\",\n    props: mergeProps<\"a\">(\n      {\n        className: cn(\n          \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n          className\n        ),\n      },\n      props\n    ),\n    render,\n    state: {\n      slot: \"sidebar-menu-sub-button\",\n      sidebar: \"menu-sub-button\",\n      size,\n      active: isActive,\n    },\n  });\n}\n\n/**\n * A label that fades out instead of disappearing when the sidebar collapses.\n * Use it for text nested deeper than a direct child of a menu button.\n */\nfunction SidebarLabel({ className, children }: React.ComponentProps<\"span\">) {\n  return (\n    <span className={cn(\"min-w-0 whitespace-nowrap\", className)}>\n      <span\n        data-slot=\"sidebar-label\"\n        className=\"block truncate transition-[opacity,translate] duration-150 ease-(--sidebar-ease) group-data-[collapsible=icon]:translate-x-0.5 group-data-[collapsible=icon]:opacity-0 motion-reduce:transition-none\"\n      >\n        {children}\n      </span>\n    </span>\n  );\n}\n\nexport {\n  Sidebar,\n  SidebarContent,\n  SidebarFooter,\n  SidebarGroup,\n  SidebarGroupAction,\n  SidebarGroupContent,\n  SidebarGroupLabel,\n  SidebarHeader,\n  SidebarInput,\n  SidebarInset,\n  SidebarLabel,\n  SidebarMenu,\n  SidebarMenuAction,\n  SidebarMenuBadge,\n  SidebarMenuButton,\n  SidebarMenuCollapsible,\n  SidebarMenuItem,\n  SidebarMenuSkeleton,\n  SidebarMenuSub,\n  SidebarMenuSubButton,\n  SidebarMenuSubItem,\n  SidebarMenuSubPanel,\n  SidebarProvider,\n  SidebarRail,\n  SidebarSeparator,\n  SidebarTrigger,\n  sidebarMenuButtonVariants,\n  useSidebar,\n};\n",
      "type": "registry:component",
      "target": "components/sidebar/components/sidebar.tsx"
    }
  ],
  "type": "registry:component"
}