{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sidebar",
  "type": "registry:ui",
  "title": "Sidebar",
  "description": "An app's side place for moving between places: sections of items with one highlight that glides to the current place; it collapses to a rail (words first, then width) with tooltips.",
  "dependencies": [],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/components/sidebar/sidebar.tsx",
      "type": "registry:ui",
      "target": "components/metalui/sidebar/sidebar.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { useRender } from '@base-ui/react/use-render';\nimport { SlidingIndicator } from '../../motion/indicator';\nimport { Tooltip } from '../tooltip/tooltip';\n\n/* ─────────────────────────────────────────────────────────\n * SIDEBAR, an app's side place for moving between places\n *\n *   rest      a header, sections under engraved titles, items (a glyph and a word), a footer\n *   current   one lifted highlight under the current item (aria-current=\"page\")\n *   choose    the highlight glides to the newly chosen item on the settle spring (free travel)\n *   collapse  to a rail: the words fade out on the release spring first, then the width settles to\n *             the rail; expanding, the width grows first and the words fade back in\n *   rail      each item keeps its name for assistive tech and shows it in a tooltip\n * Reduce Motion: width and words change at once; the highlight moves at once.\n * A place: it holds the ways to other places. It uses the row's lift, the label and the tooltip.\n * Slots: Sidebar.Root, Sidebar.Header, Sidebar.Section, Sidebar.Item, Sidebar.Footer, Sidebar.Toggle.\n * ───────────────────────────────────────────────────────── */\n\nconst ROOT = 'mu-sidebar relative flex h-full flex-none flex-col gap-sidebar-gap p-sidebar-pad overflow-hidden sidebar-width';\nconst LIST = 'mu-sidebar-list relative flex flex-1 flex-col gap-sidebar-gap min-h-0 overflow-y-auto overflow-x-hidden';\nconst SECTION = 'mu-sidebar-section flex flex-col gap-sidebar-section-gap';\nconst TITLE = 'mu-sidebar-title px-sidebar-section-title-pad type-label engraved sidebar-words';\nconst ITEM = 'mu-sidebar-item relative z-1 flex items-center overflow-hidden gap-sidebar-item-gap h-sidebar-item-height px-sidebar-item-pad-x rounded-sidebar-item-radius type-ui text-ink2 no-underline outline-none transition-colors duration-settle hover:text-ink aria-[current=page]:text-ink focus-visible:focus-ring [&>svg]:size-sidebar-item-glyph [&>svg]:flex-none';\nconst WORDS = 'mu-sidebar-words min-w-0 sidebar-words';\nconst GLIDE = 'rounded-sidebar-item-radius recipe-row-list-hover';\nconst EDGE = 'mu-sidebar-edge flex flex-col gap-sidebar-section-gap';\n\nconst Ctx = React.createContext({ collapsed: false });\n\nexport interface SidebarProps {\n  /** A rail of glyphs instead of glyphs and words. */\n  collapsed?: boolean;\n  /** Names the navigation: \"Spaces\". */\n  'aria-label': string;\n  children: React.ReactNode;\n  className?: string;\n}\n\nfunction Root({ collapsed = false, className, children, ...aria }: SidebarProps) {\n  // Header and footer stay put; the sections scroll between them.\n  const kids = React.Children.toArray(children);\n  const head = kids.filter((k) => React.isValidElement(k) && k.type === Header);\n  const foot = kids.filter((k) => React.isValidElement(k) && k.type === Footer);\n  const body = kids.filter((k) => !head.includes(k) && !foot.includes(k));\n  return (\n    <Ctx.Provider value={{ collapsed }}>\n      <nav aria-label={aria['aria-label']} data-collapsed={collapsed ? '' : undefined} className={className ? `${ROOT} ${className}` : ROOT}>\n        {head}\n        <div className={LIST}>\n          <SlidingIndicator spring=\"settle\" className={GLIDE} />\n          {body}\n        </div>\n        {foot}\n      </nav>\n    </Ctx.Provider>\n  );\n}\n\nfunction Header({ children }: { children: React.ReactNode }) {\n  return <div className={EDGE}>{children}</div>;\n}\n\nfunction Footer({ children }: { children: React.ReactNode }) {\n  return <div className={EDGE}>{children}</div>;\n}\n\nfunction Section({ title, children }: { title?: string; children: React.ReactNode }) {\n  const id = React.useId();\n  return (\n    <div role=\"group\" aria-labelledby={title ? id : undefined} className={SECTION}>\n      {title && <span id={id} className={TITLE}>{title}</span>}\n      {children}\n    </div>\n  );\n}\n\nexport interface SidebarItemProps extends useRender.ComponentProps<'a'> {\n  /** The glyph (16), from the icon set. */\n  icon: React.ReactNode;\n  /** The word; also the item's name in the rail. */\n  children: string;\n  /** The current place. */\n  active?: boolean;\n}\n\nfunction Item({ icon, children, active, render, className, ...props }: SidebarItemProps) {\n  const { collapsed } = React.useContext(Ctx);\n  const el = useRender({\n    render,\n    defaultTagName: 'a',\n    props: {\n      ...props,\n      'aria-current': active ? 'page' : undefined,\n      'aria-label': collapsed ? children : undefined,\n      className: className ? `${ITEM} ${className}` : ITEM,\n      children: (\n        <>\n          <span aria-hidden className=\"inline-grid flex-none\">{icon}</span>\n          <span className={WORDS}>{children}</span>\n        </>\n      ),\n    },\n  });\n  // Always the same tree (a tooltip disabled while expanded), so collapsing fades the words in place.\n  return <Tooltip label={children} side=\"right\" disabled={!collapsed}>{el}</Tooltip>;\n}\n\nexport interface SidebarToggleProps {\n  collapsed: boolean;\n  onCollapsedChange: (collapsed: boolean) => void;\n  icon: React.ReactNode;\n}\n\n/** Collapses the sidebar to a rail and back. */\nfunction Toggle({ collapsed, onCollapsedChange, icon }: SidebarToggleProps) {\n  const label = collapsed ? 'Expand the sidebar' : 'Collapse to a rail';\n  const button = (\n    <button type=\"button\" aria-label={label} aria-expanded={!collapsed} className={`${ITEM} border-0 bg-transparent cursor-pointer`} onClick={() => onCollapsedChange(!collapsed)}>\n      <span aria-hidden className=\"inline-grid flex-none\">{icon}</span>\n      <span className={WORDS}>{collapsed ? 'Expand' : 'Collapse'}</span>\n    </button>\n  );\n  return <Tooltip label={label} side=\"right\" disabled={!collapsed}>{button}</Tooltip>;\n}\n\nexport const Sidebar = Object.assign(Root, { Header, Section, Item, Footer, Toggle, Root });\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/sidebar.md. SwiftUI: MetalSidebar in the MetalUI Swift package."
}
