{
  "$schema": "https://ethereal-ui.saefulrohman.dev/schema/registry-item.json",
  "name": "sidebar",
  "title": "Sidebar",
  "type": "registry:blocks",
  "description": "Collapsible application sidebar with workspace switcher, nested sub-menus, badge counters, and user profile footer.",
  "dependencies": [
    "lucide-react"
  ],
  "usage": "import { SidebarShell } from \"@etherealui/react\"\n\nexport function SidebarBlockDemo() {\n  return <SidebarShell />\n}",
  "files": [
    {
      "path": "packages/ui/src/blocks/app-shell.tsx",
      "target": "components/ui/sidebar.tsx",
      "type": "registry:blocks",
      "content": "import * as React from \"react\"\nimport {\n  LayoutDashboard,\n  Users,\n  Settings,\n  Bell,\n  Search,\n  ChevronRight,\n  ChevronDown,\n  PanelLeftClose,\n  PanelLeftOpen,\n  Layers,\n  CreditCard,\n  FolderKanban,\n  BarChart3,\n  Menu,\n  X,\n} from \"lucide-react\"\nimport { cn } from \"../utils/cn\"\nimport { Avatar } from \"../primitives/avatar\"\nimport { Badge } from \"../primitives/badge\"\nimport { Button } from \"../primitives/button\"\nimport { CommandPalette } from \"../primitives/command-palette\"\n\nexport interface SidebarNavItem {\n  id: string\n  label: string\n  icon?: React.ReactNode\n  badge?: string\n  active?: boolean\n  children?: Array<{ id: string; label: string; active?: boolean }>\n}\n\nconst defaultNavItems: SidebarNavItem[] = [\n  {\n    id: \"overview\",\n    label: \"Overview\",\n    icon: <LayoutDashboard className=\"h-4 w-4\" />,\n    active: true,\n  },\n  {\n    id: \"analytics\",\n    label: \"Analytics\",\n    icon: <BarChart3 className=\"h-4 w-4\" />,\n    children: [\n      { id: \"revenue\", label: \"Revenue & MRR\", active: true },\n      { id: \"retention\", label: \"Cohort Retention\" },\n      { id: \"funnels\", label: \"Conversion Funnels\" },\n    ],\n  },\n  {\n    id: \"projects\",\n    label: \"Projects\",\n    icon: <FolderKanban className=\"h-4 w-4\" />,\n    badge: \"12\",\n  },\n  {\n    id: \"customers\",\n    label: \"Customers\",\n    icon: <Users className=\"h-4 w-4\" />,\n  },\n  {\n    id: \"billing\",\n    label: \"Billing\",\n    icon: <CreditCard className=\"h-4 w-4\" />,\n  },\n  {\n    id: \"settings\",\n    label: \"Settings\",\n    icon: <Settings className=\"h-4 w-4\" />,\n  },\n]\n\nexport interface SidebarShellProps {\n  items?: SidebarNavItem[]\n  activeId?: string\n  onSelect?: (id: string) => void\n  defaultCollapsed?: boolean\n  workspaceName?: string\n  workspaceTier?: string\n  className?: string\n}\n\nexport const SidebarShell: React.FC<SidebarShellProps> = ({\n  items = defaultNavItems,\n  activeId,\n  onSelect,\n  defaultCollapsed = false,\n  workspaceName = \"Acme Corp\",\n  workspaceTier = \"Pro\",\n  className,\n}) => {\n  const [collapsed, setCollapsed] = React.useState(defaultCollapsed)\n  const [selectedId, setSelectedId] = React.useState(activeId || \"overview\")\n  const [openGroups, setOpenGroups] = React.useState<Record<string, boolean>>({\n    analytics: true,\n  })\n\n  const currentId = activeId !== undefined ? activeId : selectedId\n\n  const handleSelect = (id: string) => {\n    setSelectedId(id)\n    onSelect?.(id)\n  }\n\n  return (\n    <aside\n      className={cn(\n        \"flex flex-col border-r border-zinc-200 bg-zinc-50/70 text-zinc-700 transition-all duration-200 select-none shrink-0 dark:border-zinc-800 dark:bg-zinc-900/60 dark:text-zinc-200\",\n        collapsed ? \"w-16\" : \"w-60\",\n        className\n      )}\n    >\n      {/* Workspace Header */}\n      <div className=\"flex h-14 items-center justify-between border-b border-zinc-200 px-3.5 dark:border-zinc-800\">\n        <div className=\"flex items-center gap-2.5 min-w-0\">\n          <div className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-blue-600 text-white\">\n            <Layers className=\"h-4 w-4\" />\n          </div>\n          {!collapsed && (\n            <div className=\"min-w-0\">\n              <div className=\"flex items-center gap-1.5\">\n                <p className=\"truncate text-xs font-semibold text-zinc-900 dark:text-zinc-100\">\n                  {workspaceName}\n                </p>\n                <span className=\"rounded bg-blue-50 px-1.5 py-0.5 text-[10px] font-medium text-blue-700 dark:bg-blue-500/15 dark:text-blue-300\">\n                  {workspaceTier}\n                </span>\n              </div>\n            </div>\n          )}\n        </div>\n        <button\n          type=\"button\"\n          onClick={() => setCollapsed(!collapsed)}\n          aria-label=\"Toggle sidebar\"\n          className=\"rounded-lg p-1.5 text-zinc-400 hover:bg-zinc-200/60 hover:text-zinc-700 dark:hover:bg-zinc-800 dark:hover:text-zinc-200 cursor-pointer\"\n        >\n          {collapsed ? (\n            <PanelLeftOpen className=\"h-4 w-4\" />\n          ) : (\n            <PanelLeftClose className=\"h-4 w-4\" />\n          )}\n        </button>\n      </div>\n\n      {/* Navigation List */}\n      <div className=\"flex-1 overflow-y-auto p-2.5 space-y-1\">\n        {!collapsed && (\n          <div className=\"px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wider text-zinc-400\">\n            Workspace\n          </div>\n        )}\n        {items.map((item) => {\n          const isActive = currentId === item.id\n          const hasChildren = Boolean(item.children && item.children.length > 0)\n          const isOpen = openGroups[item.id]\n\n          return (\n            <div key={item.id} className=\"space-y-0.5\">\n              <button\n                type=\"button\"\n                onClick={() => {\n                  handleSelect(item.id)\n                  if (hasChildren) {\n                    setOpenGroups((prev) => ({\n                      ...prev,\n                      [item.id]: !prev[item.id],\n                    }))\n                  }\n                }}\n                className={cn(\n                  \"flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-xs font-medium transition-colors cursor-pointer\",\n                  isActive\n                    ? \"bg-zinc-200/75 text-zinc-900 dark:bg-zinc-800 dark:text-white\"\n                    : \"text-zinc-600 hover:bg-zinc-200/40 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800/50 dark:hover:text-zinc-200\"\n                )}\n              >\n                <span className=\"flex items-center gap-2.5 min-w-0\">\n                  <span\n                    className={cn(\n                      \"shrink-0\",\n                      isActive\n                        ? \"text-blue-600 dark:text-blue-400\"\n                        : \"text-zinc-400\"\n                    )}\n                  >\n                    {item.icon}\n                  </span>\n                  {!collapsed && <span className=\"truncate\">{item.label}</span>}\n                </span>\n\n                {!collapsed && (\n                  <span className=\"flex items-center gap-1.5\">\n                    {item.badge && (\n                      <span className=\"rounded bg-zinc-200/80 px-1.5 py-0.5 text-[10px] text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400\">\n                        {item.badge}\n                      </span>\n                    )}\n                    {hasChildren && (\n                      <ChevronDown\n                        className={cn(\n                          \"h-3.5 w-3.5 text-zinc-400 transition-transform\",\n                          isOpen ? \"rotate-0\" : \"-rotate-90\"\n                        )}\n                      />\n                    )}\n                  </span>\n                )}\n              </button>\n\n              {!collapsed && hasChildren && isOpen && (\n                <div className=\"ml-4 border-l border-zinc-200 pl-2.5 space-y-0.5 dark:border-zinc-800\">\n                  {item.children!.map((sub) => {\n                    const subActive = currentId === sub.id\n                    return (\n                      <button\n                        key={sub.id}\n                        type=\"button\"\n                        onClick={() => handleSelect(sub.id)}\n                        className={cn(\n                          \"flex w-full items-center rounded-md px-2.5 py-1.5 text-xs transition-colors cursor-pointer\",\n                          subActive\n                            ? \"text-blue-600 font-medium dark:text-blue-400\"\n                            : \"text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200\"\n                        )}\n                      >\n                        {sub.label}\n                      </button>\n                    )\n                  })}\n                </div>\n              )}\n            </div>\n          )\n        })}\n      </div>\n\n      {/* Footer User Pod */}\n      <div className=\"border-t border-zinc-200 p-3 dark:border-zinc-800\">\n        <div className=\"flex items-center justify-between gap-2.5\">\n          <div className=\"flex items-center gap-2.5 min-w-0\">\n            <Avatar fallback=\"SR\" tint=\"blue\" size=\"sm\" status=\"online\" />\n            {!collapsed && (\n              <div className=\"min-w-0\">\n                <p className=\"truncate text-xs font-medium text-zinc-900 dark:text-zinc-100\">\n                  Saeful Rohman\n                </p>\n                <p className=\"truncate text-[11px] text-zinc-500\">\n                  saeful@acme.com\n                </p>\n              </div>\n            )}\n          </div>\n        </div>\n      </div>\n    </aside>\n  )\n}\n\nexport interface HeaderNavbarProps {\n  breadcrumbs?: string[]\n  onOpenCommandPalette?: () => void\n  onToggleMobileMenu?: () => void\n  className?: string\n}\n\nexport const HeaderNavbar: React.FC<HeaderNavbarProps> = ({\n  breadcrumbs = [\"Workspace\", \"Analytics\", \"Overview\"],\n  onOpenCommandPalette,\n  onToggleMobileMenu,\n  className,\n}) => {\n  const [internalCmdOpen, setInternalCmdOpen] = React.useState(false)\n\n  const handleOpenSearch = () => {\n    if (onOpenCommandPalette) {\n      onOpenCommandPalette()\n    } else {\n      setInternalCmdOpen(true)\n    }\n  }\n\n  return (\n    <>\n      <header\n        className={cn(\n          \"flex h-14 w-full items-center justify-between gap-2 border-b border-zinc-200 bg-white px-3.5 sm:px-5 dark:border-zinc-800 dark:bg-zinc-900\",\n          className\n        )}\n      >\n        <div className=\"flex items-center gap-2 min-w-0\">\n          {onToggleMobileMenu && (\n            <button\n              type=\"button\"\n              onClick={onToggleMobileMenu}\n              aria-label=\"Toggle workspace navigation\"\n              className=\"flex md:hidden h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-zinc-200 bg-white text-zinc-600 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300 cursor-pointer\"\n            >\n              <Menu className=\"h-4 w-4\" />\n            </button>\n          )}\n\n          {/* Breadcrumb Trail */}\n          <nav aria-label=\"Breadcrumb\" className=\"flex items-center gap-1 sm:gap-1.5 text-xs min-w-0 overflow-hidden\">\n            {breadcrumbs.map((crumb, idx) => {\n              const isLast = idx === breadcrumbs.length - 1\n              return (\n                <React.Fragment key={crumb}>\n                  {idx > 0 && (\n                    <ChevronRight\n                      className={cn(\n                        \"h-3.5 w-3.5 text-zinc-400 shrink-0\",\n                        idx < breadcrumbs.length - 1 && \"hidden sm:block\"\n                      )}\n                    />\n                  )}\n                  <span\n                    className={cn(\n                      \"truncate\",\n                      idx < breadcrumbs.length - 1 && \"hidden sm:inline\",\n                      isLast\n                        ? \"font-medium text-zinc-900 dark:text-zinc-100\"\n                        : \"text-zinc-500 dark:text-zinc-400\"\n                    )}\n                  >\n                    {crumb}\n                  </span>\n                </React.Fragment>\n              )\n            })}\n          </nav>\n        </div>\n\n        {/* Right Controls */}\n        <div className=\"flex items-center gap-2 shrink-0\">\n          <button\n            type=\"button\"\n            onClick={handleOpenSearch}\n            aria-label=\"Quick search\"\n            className=\"flex sm:hidden h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 bg-zinc-50 text-zinc-500 hover:text-zinc-800 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300 cursor-pointer\"\n          >\n            <Search className=\"h-3.5 w-3.5\" />\n          </button>\n\n          <button\n            type=\"button\"\n            onClick={handleOpenSearch}\n            className=\"hidden sm:flex h-8 w-44 md:w-52 items-center justify-between rounded-lg border border-zinc-200 bg-zinc-50 px-2.5 text-xs text-zinc-500 transition-colors hover:border-zinc-300 hover:text-zinc-800 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:border-zinc-600 dark:hover:text-zinc-100 cursor-pointer\"\n          >\n            <span className=\"flex items-center gap-2\">\n              <Search className=\"h-3.5 w-3.5 text-zinc-400 dark:text-zinc-400\" />\n              <span>Quick search...</span>\n            </span>\n            <kbd className=\"rounded border border-zinc-200 bg-white px-1.5 py-0.5 font-mono text-[10px] text-zinc-500 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-300\">\n              ⌘K\n            </kbd>\n          </button>\n\n          <button\n            type=\"button\"\n            aria-label=\"Notifications\"\n            className=\"relative flex h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 bg-white text-zinc-500 hover:bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700 cursor-pointer\"\n          >\n            <Bell className=\"h-3.5 w-3.5\" />\n            <span className=\"absolute right-1.5 top-1.5 h-1.5 w-1.5 rounded-full bg-blue-600\" />\n          </button>\n        </div>\n      </header>\n\n      {!onOpenCommandPalette && (\n        <CommandPalette\n          open={internalCmdOpen}\n          onOpenChange={setInternalCmdOpen}\n          enableGlobalShortcut={false}\n        />\n      )}\n    </>\n  )\n}\n\nexport interface PageHeaderBarProps {\n  title: string\n  subtitle?: string\n  badge?: string\n  actions?: React.ReactNode\n  className?: string\n}\n\nexport const PageHeaderBar: React.FC<PageHeaderBarProps> = ({\n  title,\n  subtitle,\n  badge,\n  actions,\n  className,\n}) => {\n  return (\n    <div\n      className={cn(\n        \"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between border-b border-zinc-200 pb-5 dark:border-zinc-800\",\n        className\n      )}\n    >\n      <div className=\"space-y-1\">\n        <div className=\"flex items-center gap-2.5\">\n          <h2 className=\"text-lg font-semibold tracking-tight text-zinc-900 dark:text-zinc-100\">\n            {title}\n          </h2>\n          {badge && (\n            <Badge variant=\"blue\" dot>\n              {badge}\n            </Badge>\n          )}\n        </div>\n        {subtitle && (\n          <p className=\"text-xs text-zinc-500 dark:text-zinc-400\">{subtitle}</p>\n        )}\n      </div>\n      <div className=\"flex items-center gap-2\">\n        {actions || (\n          <>\n            <Button variant=\"outline\" size=\"sm\">\n              Export report\n            </Button>\n            <Button variant=\"primary\" size=\"sm\">\n              New report\n            </Button>\n          </>\n        )}\n      </div>\n    </div>\n  )\n}\n\nexport interface AppShellProps {\n  workspaceName?: string\n  planBadge?: string\n  breadcrumbs?: string[]\n  user?: {\n    name: string\n    email: string\n    initials: string\n    avatarSrc?: string\n  }\n  children?: React.ReactNode\n  className?: string\n}\n\nexport const AppShell: React.FC<AppShellProps> = ({\n  workspaceName = \"Acme Corp\",\n  planBadge = \"Pro\",\n  breadcrumbs = [\"Workspace\", \"Overview\"],\n  children,\n  className,\n}) => {\n  const [mobileMenuOpen, setMobileMenuOpen] = React.useState(false)\n\n  return (\n    <div\n      className={cn(\n        \"relative flex min-h-[560px] sm:min-h-[600px] w-full overflow-hidden rounded-2xl border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950\",\n        className\n      )}\n    >\n      <SidebarShell\n        workspaceName={workspaceName}\n        workspaceTier={planBadge}\n        className=\"hidden md:flex\"\n      />\n\n      {mobileMenuOpen && (\n        <div className=\"absolute inset-0 z-30 flex md:hidden\">\n          <div\n            className=\"fixed inset-0 bg-black/50 backdrop-blur-xs\"\n            onClick={() => setMobileMenuOpen(false)}\n          />\n          <div className=\"relative z-10 flex h-full\">\n            <SidebarShell\n              workspaceName={workspaceName}\n              workspaceTier={planBadge}\n              className=\"h-full bg-white dark:bg-zinc-900 shadow-xl\"\n            />\n            <button\n              type=\"button\"\n              onClick={() => setMobileMenuOpen(false)}\n              aria-label=\"Close sidebar\"\n              className=\"m-2 flex h-8 w-8 items-center justify-center rounded-lg bg-zinc-900/80 text-white\"\n            >\n              <X className=\"h-4 w-4\" />\n            </button>\n          </div>\n        </div>\n      )}\n\n      <div className=\"flex flex-1 flex-col min-w-0\">\n        <HeaderNavbar\n          breadcrumbs={breadcrumbs}\n          onToggleMobileMenu={() => setMobileMenuOpen(true)}\n        />\n        <main className=\"flex-1 p-3.5 sm:p-6 space-y-5 sm:space-y-6 overflow-y-auto\">\n          {children}\n        </main>\n      </div>\n    </div>\n  )\n}\n"
    }
  ]
}