{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sidebar",
  "title": "Sidebar",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "https://ui.hotfix.jobs/r/recipes.json",
    "https://ui.hotfix.jobs/r/sheet.json",
    "https://ui.hotfix.jobs/r/tokens.json",
    "https://ui.hotfix.jobs/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/components/ui/sidebar.tsx",
      "content": "\"use client\";\n\nimport {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n} from \"react\";\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { selectionFocus } from \"@/lib/recipes\";\nimport { Sheet, SheetContent } from \"@/components/ui/sheet\";\n\n/* --------------------------- Context / hook --------------------------- */\n\ninterface SidebarContextValue {\n  /** Desktop (lg+) open/closed. */\n  open: boolean;\n  setOpen: (open: boolean) => void;\n  /** Mobile drawer open/closed. Tracked separately from desktop. */\n  openMobile: boolean;\n  setOpenMobile: (open: boolean) => void;\n  /** Toggles the appropriate state based on current viewport. */\n  toggle: () => void;\n  /** `left` (default) or `right`. */\n  side: \"left\" | \"right\";\n}\n\nconst SidebarContext = createContext<SidebarContextValue | null>(null);\n\nexport function useSidebar(): SidebarContextValue {\n  const ctx = useContext(SidebarContext);\n  if (!ctx) {\n    throw new Error(\"useSidebar must be used within a SidebarProvider\");\n  }\n  return ctx;\n}\n\nexport interface SidebarProviderProps {\n  /** Initial open state. Default true (visible on desktop). */\n  defaultOpen?: boolean;\n  /** Controlled open state (desktop). */\n  open?: boolean;\n  /** Called when the desktop open state changes. */\n  onOpenChange?: (open: boolean) => void;\n  side?: \"left\" | \"right\";\n  /** Toggle shortcut, e.g. `mod+b`. Set `null` to disable. */\n  shortcut?: string | null;\n  children: React.ReactNode;\n}\n\n/** Provides sidebar open state and toggle shortcut. */\nexport function SidebarProvider({\n  defaultOpen = true,\n  open: openProp,\n  onOpenChange,\n  side = \"left\",\n  shortcut = \"mod+b\",\n  children,\n}: SidebarProviderProps): React.ReactElement {\n  const [uncontrolled, setUncontrolled] = useState(defaultOpen);\n  const open = openProp ?? uncontrolled;\n  const setOpen = useCallback(\n    (next: boolean) => {\n      if (openProp === undefined) setUncontrolled(next);\n      onOpenChange?.(next);\n    },\n    [openProp, onOpenChange],\n  );\n\n  const [openMobile, setOpenMobile] = useState(false);\n\n  const toggle = useCallback(() => {\n    if (typeof window !== \"undefined\" && window.innerWidth < 1024) {\n      setOpenMobile((v) => !v);\n    } else {\n      setOpen(!open);\n    }\n  }, [open, setOpen]);\n\n  useEffect(() => {\n    if (!shortcut) return;\n    const match = /^mod\\+(.+)$/i.exec(shortcut);\n    if (!match) return;\n    const key = match[1].toLowerCase();\n    const handler = (e: KeyboardEvent) => {\n      if (!(e.metaKey || e.ctrlKey)) return;\n      if (e.key.toLowerCase() !== key) return;\n      e.preventDefault();\n      toggle();\n    };\n    window.addEventListener(\"keydown\", handler);\n    return () => window.removeEventListener(\"keydown\", handler);\n  }, [shortcut, toggle]);\n\n  const value = useMemo<SidebarContextValue>(\n    () => ({ open, setOpen, openMobile, setOpenMobile, toggle, side }),\n    [open, setOpen, openMobile, toggle, side],\n  );\n\n  return (\n    <SidebarContext.Provider value={value}>\n      <div\n        data-slot=\"sidebar-wrapper\"\n        className=\"flex min-h-svh w-full\"\n      >\n        {children}\n      </div>\n    </SidebarContext.Provider>\n  );\n}\n\n/* ------------------------------ Sidebar ------------------------------ */\n\nexport interface SidebarProps extends useRender.ComponentProps<\"aside\"> {\n  collapsible?: \"offcanvas\" | \"none\";\n  /** Round the desktop sidebar surface. */\n  rounded?: boolean;\n  /** Add a hairline boundary around the desktop sidebar surface. */\n  bordered?: boolean;\n  /** Fixed pixel width. */\n  width?: number;\n  /** Offset from top of viewport, in pixels. */\n  topOffset?: number;\n}\n\n/** Fixed-position column on desktop, slide-out drawer on mobile. */\nexport function Sidebar({\n  collapsible = \"offcanvas\",\n  rounded = false,\n  bordered = false,\n  width = 256,\n  topOffset = 0,\n  className,\n  children,\n  ...props\n}: SidebarProps): React.ReactElement {\n  const { open, openMobile, setOpenMobile, side } = useSidebar();\n  const collapsed = collapsible === \"offcanvas\" && !open;\n\n  const dimensionVars = {\n    [\"--sidebar-width\" as string]: `${width}px`,\n    [\"--sidebar-top\" as string]: `${topOffset}px`,\n  } as React.CSSProperties;\n\n  return (\n    <>\n      <div\n        data-slot=\"sidebar-container\"\n        data-state={open ? \"open\" : \"closed\"}\n        data-side={side}\n        style={dimensionVars}\n        className={cn(\n          \"group peer hidden lg:block\",\n          collapsed && \"lg:w-0\",\n        )}\n      >\n        <div\n          className={cn(\n            \"relative h-svh w-[var(--sidebar-width)] transition-[width] duration-[var(--duration-overlay)] ease-[var(--ease-standard)]\",\n            collapsed && \"w-0\",\n          )}\n        />\n        <aside\n          data-slot=\"sidebar\"\n          className={cn(\n            \"fixed inset-y-0 z-30 flex w-[var(--sidebar-width)] flex-col bg-layer-1 transition-[transform,left,right] duration-[var(--duration-overlay)] ease-[var(--ease-standard)]\",\n            \"top-[var(--sidebar-top)] h-[calc(100svh-var(--sidebar-top))]\",\n            side === \"left\" ? \"left-0\" : \"right-0\",\n            rounded && \"overflow-hidden rounded-[var(--radius-12)]\",\n            bordered && \"border border-hairline\",\n            collapsed &&\n              (side === \"left\" ? \"-translate-x-full\" : \"translate-x-full\"),\n            className,\n          )}\n          {...props}\n        >\n          {children}\n        </aside>\n      </div>\n\n      <Sheet open={openMobile} onOpenChange={setOpenMobile} side={side}>\n        <SheetContent className=\"w-64 p-0 lg:hidden\">\n          <div\n            data-slot=\"sidebar-mobile\"\n            style={dimensionVars}\n            className=\"flex h-full w-full flex-col\"\n          >\n            {children}\n          </div>\n        </SheetContent>\n      </Sheet>\n    </>\n  );\n}\n\n/* ------------------------------- Inset ------------------------------- */\n\nexport type SidebarInsetProps = React.HTMLAttributes<HTMLElement>;\n\n/** Main content column that sits alongside the sidebar. */\nexport function SidebarInset({\n  className,\n  ...props\n}: SidebarInsetProps): React.ReactElement {\n  return (\n    <main\n      data-slot=\"sidebar-inset\"\n      className={cn(\n        \"relative flex min-h-svh w-full flex-1 flex-col bg-base\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/* --------------------------- Compound slots --------------------------- */\n\nexport type SidebarHeaderProps = React.HTMLAttributes<HTMLDivElement>;\n\nexport function SidebarHeader({\n  className,\n  ...props\n}: SidebarHeaderProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sidebar-header\"\n      className={cn(\"flex shrink-0 flex-col gap-2 p-3\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type SidebarContentProps = React.HTMLAttributes<HTMLDivElement>;\n\nexport function SidebarContent({\n  className,\n  ...props\n}: SidebarContentProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sidebar-content\"\n      className={cn(\n        \"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-3\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport type SidebarFooterProps = React.HTMLAttributes<HTMLDivElement>;\n\nexport function SidebarFooter({\n  className,\n  ...props\n}: SidebarFooterProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sidebar-footer\"\n      className={cn(\n        \"flex shrink-0 flex-col gap-2 p-3\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/* -------------------------------- Group ------------------------------- */\n\nexport type SidebarGroupProps = React.HTMLAttributes<HTMLDivElement>;\n\nexport function SidebarGroup({\n  className,\n  ...props\n}: SidebarGroupProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sidebar-group\"\n      className={cn(\"flex flex-col gap-1\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type SidebarGroupLabelProps = React.HTMLAttributes<HTMLDivElement>;\n\nexport function SidebarGroupLabel({\n  className,\n  ...props\n}: SidebarGroupLabelProps): React.ReactElement {\n  return (\n    <div\n      data-slot=\"sidebar-group-label\"\n      className={cn(\"px-2 pt-2 text-mini font-medium text-ink-muted\", className)}\n      {...props}\n    />\n  );\n}\n\n/* -------------------------------- Menu -------------------------------- */\n\nexport type SidebarMenuProps = React.HTMLAttributes<HTMLUListElement>;\n\nexport function SidebarMenu({\n  className,\n  ...props\n}: SidebarMenuProps): React.ReactElement {\n  return (\n    <ul\n      data-slot=\"sidebar-menu\"\n      className={cn(\"flex flex-col gap-0.5\", className)}\n      {...props}\n    />\n  );\n}\n\nexport type SidebarMenuItemProps = React.LiHTMLAttributes<HTMLLIElement>;\n\nexport function SidebarMenuItem({\n  className,\n  ...props\n}: SidebarMenuItemProps): React.ReactElement {\n  return (\n    <li\n      data-slot=\"sidebar-menu-item\"\n      className={cn(\"\", className)}\n      {...props}\n    />\n  );\n}\n\nexport interface SidebarMenuButtonProps\n  extends useRender.ComponentProps<\"button\"> {\n  /** Currently active nav item. Sets aria-current=\"page\" and highlights. */\n  active?: boolean;\n}\n\nexport function SidebarMenuButton({\n  active,\n  className,\n  render,\n  ...props\n}: SidebarMenuButtonProps): React.ReactElement {\n  const defaultProps = {\n    \"data-slot\": \"sidebar-menu-button\",\n    \"data-active\": active || undefined,\n    \"aria-current\": active ? (\"page\" as const) : undefined,\n    className: cn(\n      \"flex w-full items-center gap-2 rounded-[var(--radius-8)] px-2 py-1.5 text-small transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)]\",\n      active\n        ? \"bg-layer-hover font-medium text-ink\"\n        : \"text-ink-muted hover:bg-layer-hover hover:text-ink\",\n      \"active:bg-layer-hover disabled:pointer-events-none disabled:opacity-50\",\n      selectionFocus,\n      className,\n    ),\n  };\n  return useRender({\n    render: render ?? <button type=\"button\" />,\n    props: mergeProps<\"button\">(defaultProps, props),\n  });\n}\n\n/* ------------------------------- Trigger ------------------------------ */\n\nexport type SidebarTriggerProps = React.ButtonHTMLAttributes<HTMLButtonElement>;\n\nexport function SidebarTrigger({\n  className,\n  onClick,\n  ...props\n}: SidebarTriggerProps): React.ReactElement {\n  const { toggle } = useSidebar();\n  return (\n    <button\n      type=\"button\"\n      data-slot=\"sidebar-trigger\"\n      aria-label=\"Toggle sidebar\"\n      onClick={(e) => {\n        onClick?.(e);\n        if (!e.defaultPrevented) toggle();\n      }}\n      className={cn(\n        \"inline-flex size-9 items-center justify-center rounded-[var(--radius-8)] text-ink transition-colors duration-[var(--duration-state)] ease-[var(--ease-standard)] hover:bg-layer-hover active:bg-layer-hover disabled:pointer-events-none disabled:opacity-50\",\n        selectionFocus,\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/sidebar.tsx"
    }
  ],
  "type": "registry:ui"
}