{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "breadcrumb-icon",
  "type": "registry:ui",
  "title": "Breadcrumb Icon",
  "description": "Breadcrumb navigation with icons for each item.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/ruixenui/breadcrumb-icon.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Home, type LucideIcon } from \"lucide-react\";\n\n/* ═══════════════════════════════════════════════════════════\n   Breadcrumb Icon — Magnetic pill-tracking breadcrumb.\n\n   Flips the cascade metaphor on its head. Instead of dimming\n   everything else when you hover, a soft pill highlight\n   slides TO the item you're pointing at — the highlight\n   comes to you. The hovered icon does a spring micro-bounce\n   for tactile feedback without disturbing its neighbors.\n\n   Two fundamentally different interaction approaches:\n     • breadcrumb-separator: hover affects OTHER items (cascade)\n     • breadcrumb-icon: hover affects ONLY the target (pill tracks)\n\n   Curves:\n     • Pill slide: cubic-bezier(0.33, 1.52, 0.58, 1) — spring overshoot\n     • Icon pop:   cubic-bezier(0.34, 1.56, 0.64, 1) — tactile bounce\n   ═══════════════════════════════════════════════════════════ */\n\nexport interface BreadcrumbIconItem {\n  label: string;\n  href?: string;\n  icon?: LucideIcon;\n}\n\nexport interface BreadcrumbIconProps {\n  items: BreadcrumbIconItem[];\n  showHomeIcon?: boolean;\n  iconOnly?: boolean;\n  className?: string;\n}\n\nexport function BreadcrumbIcon({\n  items,\n  showHomeIcon = true,\n  iconOnly = false,\n  className,\n}: BreadcrumbIconProps) {\n  const [hoveredIndex, setHoveredIndex] = React.useState<number | null>(null);\n  const [pill, setPill] = React.useState({ left: 0, width: 0 });\n  const containerRef = React.useRef<HTMLOListElement>(null);\n  const itemRefs = React.useRef<(HTMLElement | null)[]>([]);\n  const hasPositioned = React.useRef(false);\n\n  const itemsWithIcons = items.map((item, index) => ({\n    ...item,\n    icon: item.icon || (index === 0 && showHomeIcon ? Home : undefined),\n  }));\n\n  const handleHover = React.useCallback((index: number) => {\n    setHoveredIndex(index);\n    const el = itemRefs.current[index];\n    const container = containerRef.current;\n    if (el && container) {\n      const containerRect = container.getBoundingClientRect();\n      const elRect = el.getBoundingClientRect();\n      setPill({\n        left: elRect.left - containerRect.left,\n        width: elRect.width,\n      });\n    }\n  }, []);\n\n  React.useEffect(() => {\n    if (hoveredIndex !== null) {\n      hasPositioned.current = true;\n    }\n  }, [hoveredIndex]);\n\n  if (items.length === 0) return null;\n\n  return (\n    <nav aria-label=\"Breadcrumb\" className={cn(\"\", className)}>\n      <ol\n        ref={containerRef}\n        className=\"relative flex flex-wrap items-center gap-0.5\"\n        onMouseLeave={() => setHoveredIndex(null)}\n      >\n        {/* Magnetic pill — slides to the hovered item */}\n        <div\n          className=\"pointer-events-none absolute rounded-md bg-foreground/[0.06]\"\n          style={{\n            left: pill.left,\n            width: pill.width,\n            top: 0,\n            bottom: 0,\n            opacity: hoveredIndex !== null ? 1 : 0,\n            transition:\n              hoveredIndex !== null && hasPositioned.current\n                ? \"left 0.35s cubic-bezier(0.33, 1.52, 0.58, 1), width 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.15s ease\"\n                : \"opacity 0.2s ease\",\n          }}\n        />\n\n        {itemsWithIcons.map((item, index) => {\n          const isLast = index === items.length - 1;\n          const isLink = !!item.href && !isLast;\n          const isHovered = hoveredIndex === index;\n          const Icon = item.icon;\n\n          const iconEl = Icon ? (\n            <Icon\n              className=\"size-3.5\"\n              style={{\n                transform: isHovered ? \"scale(1.18)\" : \"scale(1)\",\n                transition: \"transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)\",\n              }}\n            />\n          ) : null;\n\n          return (\n            <li key={index} className=\"z-10 inline-flex items-center\">\n              {isLink ? (\n                <a\n                  ref={(el) => {\n                    itemRefs.current[index] = el;\n                  }}\n                  href={item.href}\n                  className={cn(\n                    \"inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm transition-colors duration-200\",\n                    isHovered\n                      ? \"text-foreground\"\n                      : \"text-muted-foreground hover:text-foreground\",\n                  )}\n                  onMouseEnter={() => handleHover(index)}\n                >\n                  {iconEl}\n                  {!iconOnly && <span>{item.label}</span>}\n                </a>\n              ) : (\n                <span\n                  ref={(el) => {\n                    itemRefs.current[index] = el;\n                  }}\n                  className={cn(\n                    \"inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm\",\n                    isLast\n                      ? \"font-medium text-foreground\"\n                      : \"text-muted-foreground\",\n                  )}\n                  onMouseEnter={() => setHoveredIndex(null)}\n                  {...(isLast ? { \"aria-current\": \"page\" as const } : {})}\n                >\n                  {iconEl}\n                  {!iconOnly && <span>{item.label}</span>}\n                </span>\n              )}\n              {!isLast && (\n                <span\n                  className=\"mx-0.5 inline-flex items-center text-muted-foreground/30\"\n                  aria-hidden=\"true\"\n                >\n                  <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n                    <path\n                      d=\"M4.5 3L7.5 6L4.5 9\"\n                      stroke=\"currentColor\"\n                      strokeWidth=\"1.25\"\n                      strokeLinecap=\"round\"\n                      strokeLinejoin=\"round\"\n                    />\n                  </svg>\n                </span>\n              )}\n            </li>\n          );\n        })}\n      </ol>\n    </nav>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ruixen/breadcrumb-icon.tsx"
    }
  ]
}