{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tailwind-navigationtree",
  "type": "registry:ui",
  "title": "NavigationTree",
  "dependencies": [
    "react-aria-components",
    "lucide-react",
    "react",
    "tailwind-variants",
    "tailwindcss-react-aria-components",
    "tw-animate-css"
  ],
  "registryDependencies": [
    "https://react-aria.adobe.com/registry/tailwind-utils.json"
  ],
  "files": [
    {
      "path": "components/ui/NavigationTree.tsx",
      "type": "registry:ui",
      "content": "'use client';\nimport {\n  Button,\n  Link,\n  type LinkProps,\n  NavigationTree as AriaNavigationTree,\n  NavigationTreeHeader as AriaNavigationTreeHeader,\n  type NavigationTreeHeaderProps,\n  NavigationTreeItem as AriaNavigationTreeItem,\n  NavigationTreeItemContent as AriaNavigationTreeItemContent,\n  type NavigationTreeItemProps as AriaNavigationTreeItemProps,\n  type NavigationTreeProps,\n  NavigationTreeSection as AriaNavigationTreeSection,\n  type NavigationTreeSectionProps\n} from 'react-aria-components/NavigationTree';\nimport {ChevronRight} from 'lucide-react';\nimport React from 'react';\nimport {tv} from 'tailwind-variants';\nimport {composeTailwindRenderProps, focusRing} from '@/registry/react-aria/lib/react-aria-utils';\n\nexport function NavigationTree<T>({\n  renderLink,\n  children,\n  ...props\n}: NavigationTreeProps<T> & {renderLink?: LinkProps['render']}) {\n  return (\n    <RenderLinkContext.Provider value={renderLink}>\n      <AriaNavigationTree\n        {...props}\n        className={composeTailwindRenderProps(\n          props.className,\n          'w-56 max-w-full max-h-72 overflow-auto flex flex-col p-1 gap-0.5 relative border border-neutral-200 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-900 outline-none'\n        )}>\n        {children}\n      </AriaNavigationTree>\n    </RenderLinkContext.Provider>\n  );\n}\n\n// The focus ring lives on the row (not the link) so it spans the whole item. Hover/current/ancestor\n// state is surfaced as a leading-edge indicator (see indicatorStyles) rather than a full-row\n// background. The row is a `group` so the indicator can react to the row's data-* attributes. isFocusVisible\n// comes from the render props; RAC's isFocusVisible already follows the link (it is not true when\n// another child, e.g. a button, is focused).\nconst itemStyles = tv({\n  extend: focusRing,\n  base: 'group relative font-sans flex items-center rounded-md cursor-default select-none text-neutral-800 dark:text-neutral-200 -outline-offset-2 [-webkit-tap-highlight-color:transparent]',\n  variants: {\n    isDisabled: {\n      true: 'text-neutral-300 dark:text-neutral-600 forced-colors:text-[GrayText]'\n    }\n  }\n});\n\nconst linkStyles = tv({\n  base:\n    'relative flex-1 min-w-0 flex items-center gap-2 py-1.5 px-2 text-sm no-underline text-current cursor-pointer outline-none ' +\n    // A row without an href renders its label as a non-interactive span, so it should not look clickable.\n    'group-[:not([data-href])]:cursor-default',\n  variants: {\n    isDisabled: {\n      true: 'cursor-default'\n    }\n  }\n});\n\n// The leading-edge indicator is a real, presentational element (aria-hidden). Its variant is chosen in\n// JS (see NavigationTreeItemContent) and surfaced as a single `data-indicator` attribute, so the styles\n// stay flat and precedence lives in one place instead of relying on CSS specificity:\n//   - current (blue pill): the selected row. Because the variant is picked in JS, it keeps this color\n//     even while hovered.\n//   - ancestor (neutral dot): a collapsed ancestor of the current route.\n//   - hover (neutral pill): any actionable row on hover.\nconst indicatorStyles = tv({\n  base:\n    'absolute start-0.5 top-1/2 -translate-y-1/2 w-0.5 h-[1lh] rounded-full forced-color-adjust-none ' +\n    // Only real link rows (the group row has data-href) get the hover pill.\n    'group-[[data-href]]:data-[indicator=hover]:bg-neutral-400 dark:group-[[data-href]]:data-[indicator=hover]:bg-neutral-500 ' +\n    'data-[indicator=current]:bg-blue-600 dark:data-[indicator=current]:bg-blue-400 ' +\n    'data-[indicator=ancestor]:h-1 data-[indicator=ancestor]:w-1 data-[indicator=ancestor]:bg-neutral-400 dark:data-[indicator=ancestor]:bg-neutral-500 ' +\n    // In forced-colors mode authored backgrounds are dropped, so render any visible state as Highlight.\n    'forced-colors:data-[indicator]:bg-[Highlight]'\n});\n\nconst expandButton = tv({\n  extend: focusRing,\n  base: 'shrink-0 w-6 h-6 flex items-center justify-center rounded-md border-0 p-0 bg-transparent cursor-default [-webkit-tap-highlight-color:transparent] -outline-offset-2'\n});\n\nconst chevron = tv({\n  base: 'w-4 h-4 text-neutral-500 dark:text-neutral-400 transition-transform duration-200 ease-in-out',\n  variants: {\n    isExpanded: {\n      true: 'rotate-90'\n    }\n  }\n});\n\nconst NavTreeLinkContext = React.createContext<{\n  indicator: 'current' | 'ancestor' | undefined;\n}>({indicator: undefined});\n\nconst RenderLinkContext = React.createContext<LinkProps['render'] | undefined>(undefined);\n\nexport function NavigationTreeItemContent(props: {children?: React.ReactNode}) {\n  return (\n    <AriaNavigationTreeItemContent>\n      {({level, hasChildItems, isExpanded, isCurrent, isCurrentAncestor}) => {\n        let indicator: 'current' | 'ancestor' | undefined;\n        if (isCurrent) {\n          indicator = 'current';\n        } else if (isCurrentAncestor && !isExpanded) {\n          indicator = 'ancestor';\n        }\n        return (\n          <>\n            {level > 1 && (\n              <div\n                className=\"shrink-0\"\n                style={{width: `calc((${level} - 1) * calc(var(--spacing) * 4))`}}\n              />\n            )}\n            <NavTreeLinkContext.Provider value={{indicator}}>\n              {props.children}\n            </NavTreeLinkContext.Provider>\n            <Button\n              slot=\"chevron\"\n              isDisabled={!hasChildItems}\n              className={({isFocusVisible}) =>\n                expandButton({isFocusVisible, className: hasChildItems ? undefined : 'invisible'})\n              }>\n              <ChevronRight aria-hidden className={chevron({isExpanded})} />\n            </Button>\n          </>\n        );\n      }}\n    </AriaNavigationTreeItemContent>\n  );\n}\n\nexport function NavigationTreeItem(props: AriaNavigationTreeItemProps) {\n  return <AriaNavigationTreeItem className={itemStyles} {...props} />;\n}\n\nexport function NavigationTreeSection<T extends object>(props: NavigationTreeSectionProps<T>) {\n  return <AriaNavigationTreeSection {...props} className=\"not-first:mt-4\" />;\n}\n\nexport function NavigationTreeHeader(props: NavigationTreeHeaderProps) {\n  return (\n    <AriaNavigationTreeHeader\n      {...props}\n      className=\"px-2 py-1 text-sm font-semibold text-neutral-700 dark:text-neutral-300\"\n    />\n  );\n}\n\nexport interface NavigationTreeItemLinkProps extends Omit<LinkProps, 'children'> {\n  children?: React.ReactNode;\n}\n\nexport function NavigationTreeItemLink({\n  render: renderProp,\n  ...props\n}: NavigationTreeItemLinkProps) {\n  let {indicator} = React.useContext(NavTreeLinkContext);\n  let contextRender = React.useContext(RenderLinkContext);\n  return (\n    <Link\n      render={renderProp ?? contextRender}\n      {...props}\n      className={({isDisabled}) => linkStyles({isDisabled})}>\n      {({isHovered}) => (\n        <>\n          <span\n            aria-hidden\n            data-indicator={indicator ?? (isHovered ? 'hover' : undefined)}\n            className={indicatorStyles()}\n          />\n          {props.children}\n        </>\n      )}\n    </Link>\n  );\n}\n"
    }
  ],
  "css": {
    "@plugin \"tailwindcss-react-aria-components\"": {},
    "@import \"tw-animate-css\"": {}
  }
}
