{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sidebar",
  "type": "registry:component",
  "title": "Sidebar",
  "description": "Collapsible sidebar navigation layout.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/sidebar/sidebar.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { ChevronDown } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\n\nimport { cn } from \"@vllnt/ui\";\nimport { useSidebar } from \"@vllnt/ui\";\n\nexport type SidebarItem = {\n  href: string;\n  title: string;\n};\n\nexport type SidebarSection = {\n  collapsible?: boolean;\n  defaultOpen?: boolean;\n  items: SidebarItem[];\n  title?: string;\n};\n\ntype SidebarProps = {\n  sections: SidebarSection[];\n};\n\nfunction useMobile(setOpen: (open: boolean) => void) {\n  const [isMobile, setIsMobile] = useState(false);\n\n  useEffect(() => {\n    const checkMobile = () => {\n      const mobile = window.innerWidth < 1024;\n      setIsMobile(mobile);\n      if (mobile) {\n        setOpen(false);\n      } else {\n        setOpen(true);\n      }\n    };\n\n    checkMobile();\n    window.addEventListener(\"resize\", checkMobile);\n    return () => {\n      window.removeEventListener(\"resize\", checkMobile);\n    };\n  }, [setOpen]);\n\n  return isMobile;\n}\n\nfunction useScrollFade(\n  containerReference: React.RefObject<HTMLDivElement | null>,\n) {\n  const [showTopFade, setShowTopFade] = useState(false);\n  const [showBottomFade, setShowBottomFade] = useState(false);\n\n  useEffect(() => {\n    const container = containerReference.current;\n    if (!container) return;\n\n    const checkScroll = () => {\n      const { clientHeight, scrollHeight, scrollTop } = container;\n      setShowTopFade(scrollTop > 0);\n      setShowBottomFade(scrollTop < scrollHeight - clientHeight - 1);\n    };\n\n    checkScroll();\n    container.addEventListener(\"scroll\", checkScroll);\n    return () => {\n      container.removeEventListener(\"scroll\", checkScroll);\n    };\n  }, [containerReference]);\n\n  return { showBottomFade, showTopFade };\n}\n\ntype CollapsibleSectionProps = {\n  children: React.ReactNode;\n  collapsible?: boolean;\n  defaultOpen?: boolean;\n  title: string;\n};\n\nfunction CollapsibleSection({\n  children,\n  collapsible = false,\n  defaultOpen = true,\n  title,\n}: CollapsibleSectionProps) {\n  const [isOpen, setIsOpen] = useState(defaultOpen);\n\n  if (!collapsible) {\n    return (\n      <>\n        <div className=\"px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n          {title}\n        </div>\n        {children}\n      </>\n    );\n  }\n\n  return (\n    <>\n      <button\n        className=\"flex w-full items-center justify-between px-3 py-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider hover:text-foreground transition-colors\"\n        onClick={() => {\n          setIsOpen(!isOpen);\n        }}\n        type=\"button\"\n      >\n        <span>{title}</span>\n        <ChevronDown\n          className={cn(\n            \"size-3 transition-transform duration-200\",\n            isOpen && \"rotate-180\",\n          )}\n        />\n      </button>\n      <div\n        className={cn(\n          \"grid transition-all duration-200 ease-in-out\",\n          isOpen ? \"grid-rows-[1fr] opacity-100\" : \"grid-rows-[0fr] opacity-0\",\n        )}\n      >\n        <div className=\"overflow-hidden\">{children}</div>\n      </div>\n    </>\n  );\n}\n\n// eslint-disable-next-line max-lines-per-function\nexport function Sidebar({ sections }: SidebarProps) {\n  const pathname = usePathname();\n  const { open, setOpen } = useSidebar();\n  const isMobile = useMobile(setOpen);\n  const scrollContainerReference = useRef<HTMLDivElement>(null);\n  const { showBottomFade, showTopFade } = useScrollFade(\n    scrollContainerReference,\n  );\n\n  return (\n    <>\n      {/* Mobile overlay */}\n      {isMobile && open ? (\n        <div\n          className=\"fixed inset-0 bg-black/50 z-40 lg:hidden\"\n          onClick={() => {\n            setOpen(false);\n          }}\n          onKeyDown={(event) => {\n            if (event.key === \"Escape\") {\n              setOpen(false);\n            }\n          }}\n          role=\"button\"\n          tabIndex={0}\n        />\n      ) : null}\n\n      {/* Sidebar */}\n      <aside\n        className={cn(\n          \"fixed lg:relative top-16 lg:top-0 bottom-0 lg:bottom-auto left-0 z-40 lg:h-full border-r bg-background transition-transform duration-300 ease-in-out\",\n          \"flex flex-col\",\n          \"overflow-hidden\",\n          \"shrink-0\",\n          isMobile ? \"w-full\" : \"w-64\",\n          isMobile && !open && \"-translate-x-full\",\n          isMobile && open && \"translate-x-0\",\n          !isMobile && !open && \"-translate-x-full lg:translate-x-0\",\n          !isMobile && \"lg:translate-x-0\",\n        )}\n      >\n        <div className=\"relative flex-1 overflow-hidden\">\n          {/* Top fade */}\n          {showTopFade ? (\n            <div className=\"absolute top-0 left-0 right-0 h-8 bg-gradient-to-b from-background to-transparent pointer-events-none z-20\" />\n          ) : null}\n\n          {/* Bottom fade */}\n          {showBottomFade ? (\n            <div className=\"absolute bottom-0 left-0 right-0 h-8 bg-gradient-to-t from-background to-transparent pointer-events-none z-20\" />\n          ) : null}\n\n          <nav\n            className=\"flex-1 p-4 overflow-y-auto h-full\"\n            ref={scrollContainerReference}\n          >\n            <div className=\"space-y-4\">\n              {sections.map((section, sectionIndex) => {\n                const sectionItems = (\n                  <div className={section.title ? \"space-y-0.5\" : \"space-y-1\"}>\n                    {section.items.map((item) => (\n                      <Link\n                        className={cn(\n                          section.title\n                            ? \"block px-3 py-1.5 rounded-md text-sm transition-colors\"\n                            : \"flex items-center px-3 py-2 rounded-md text-sm font-medium transition-colors\",\n                          pathname === item.href ||\n                            (item.href === \"/\" && pathname === \"/\")\n                            ? \"bg-accent text-accent-foreground\"\n                            : section.title\n                              ? \"text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n                              : \"hover:bg-accent hover:text-accent-foreground\",\n                          section.title &&\n                            pathname === item.href &&\n                            \"font-medium\",\n                        )}\n                        href={item.href}\n                        key={item.href}\n                        onClick={() => {\n                          if (isMobile) {\n                            setOpen(false);\n                          }\n                        }}\n                      >\n                        {item.title}\n                      </Link>\n                    ))}\n                  </div>\n                );\n\n                return (\n                  <div\n                    className=\"space-y-1\"\n                    key={section.title || sectionIndex}\n                  >\n                    {section.title ? (\n                      <CollapsibleSection\n                        collapsible={section.collapsible}\n                        defaultOpen={section.defaultOpen ?? true}\n                        title={section.title}\n                      >\n                        {sectionItems}\n                      </CollapsibleSection>\n                    ) : (\n                      sectionItems\n                    )}\n                  </div>\n                );\n              })}\n            </div>\n          </nav>\n        </div>\n      </aside>\n    </>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
